Multipurpose utility for managing Games for Windows - LIVE installs and content. (Mirrored from https://github.com/InvoxiPlayGames/GfWLUtility)
1using System.Collections.Generic;
2using System.Drawing;
3using System.IO;
4using System.Runtime.InteropServices;
5
6namespace GfWLUtility
7{
8 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
9 internal struct GamertagString
10 {
11 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x10)]
12 public string Value;
13
14 public static implicit operator string(GamertagString value)
15 {
16 return value.Value;
17 }
18 }
19
20 [StructLayout(LayoutKind.Sequential, Pack = 1)]
21 internal struct XamAccount
22 {
23 public uint Flags1;
24
25 public uint Flags2;
26
27 [MarshalAs(UnmanagedType.Struct)]
28 public GamertagString Gamertag;
29
30 public ulong OnlineXUID;
31
32 public uint Flags3;
33
34 public uint OnlineServiceID;
35
36 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4)]
37 public byte[] Passcode; // unused in GfWL..?
38
39 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x14)]
40 public string Domain; // unused in GfWL
41
42 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x18)]
43 public string Realm; // unused in GfWL
44
45 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
46 public byte[] OnlineKey; // unused in GfWL
47
48 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x72)]
49 public string PassportEmail;
50
51 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
52 public string PassportPassword; // unused in GfWL
53
54 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x72)]
55 public string OwnerPassportEmail; // unused in GfWL
56 }
57
58 internal class KnownUser
59 {
60 public ulong XUID;
61 public Bitmap ProfilePicture;
62 public bool HasFullInformation;
63
64 public string Gamertag;
65 public bool LiveEnabled;
66 public bool Pnet;
67 public ulong OnlineXUID;
68 public string MsaEmail;
69
70 public KnownUser(ulong xuid)
71 {
72 XUID = xuid;
73 }
74
75 public override string ToString()
76 {
77 if (Gamertag != null)
78 return Gamertag;
79 return $"{XUID:X} (Unknown)";
80 }
81 }
82
83 internal class UserManager
84 {
85 public static Dictionary<ulong, KnownUser> KnownUsers = new Dictionary<ulong, KnownUser>();
86
87 public static void FoundUserExists(ulong xuid)
88 {
89 if (!KnownUsers.ContainsKey(xuid))
90 KnownUsers[xuid] = new KnownUser(xuid);
91 }
92
93 public static void FoundUserGamertag(ulong xuid, string name)
94 {
95 FoundUserExists(xuid);
96 KnownUsers[xuid].Gamertag = name;
97 }
98
99 public static void PopulateUserInformation(ulong xuid, string accountFile = null)
100 {
101 FoundUserExists(xuid);
102 byte[] accBytes = null;
103 // if we're given one, attempt to decrypt profile information directly
104 if (accountFile != null)
105 {
106 byte[] encBytes = File.ReadAllBytes(accountFile);
107 accBytes = XeKeys.UnObfuscate(encBytes);
108 }
109 // otherwise check if we have decrypted profile information in the account cache
110 if (accBytes == null)
111 {
112 string accCacheFilename = UtilityFuncs.GetLocalDirectory("ProfileCache") + xuid.ToString("x16") + ".bin";
113 if (File.Exists(accCacheFilename))
114 accBytes = File.ReadAllBytes(accCacheFilename);
115 }
116 // if we have a byte stream of the right length of a decrypted account file, hooray
117 if (accBytes != null && accBytes.Length == 0x17C)
118 {
119 XamAccount account = UtilityFuncs.BytesToStructure<XamAccount>(accBytes);
120 KnownUsers[xuid].Gamertag = account.Gamertag;
121 if ((account.Flags1 & 0x20000000) == 0x20000000)
122 KnownUsers[xuid].LiveEnabled = true;
123 KnownUsers[xuid].OnlineXUID = account.OnlineXUID;
124 KnownUsers[xuid].Pnet = account.OnlineServiceID == 0x54524150;
125 KnownUsers[xuid].MsaEmail = account.PassportEmail;
126 KnownUsers[xuid].HasFullInformation = true;
127 }
128 }
129 }
130}