Multipurpose utility for managing Games for Windows - LIVE installs and content. (Mirrored from https://github.com/InvoxiPlayGames/GfWLUtility)
1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.IO;
5using System.Linq;
6using System.Reflection;
7using System.Security.Cryptography;
8using System.Text;
9
10namespace GfWLUtility
11{
12 internal class KnownTitle
13 {
14 public uint TitleID;
15 public string Name;
16 public string ProductKey;
17 public byte[] ConfigData;
18 public Bitmap Icon;
19
20 public KnownTitle(uint titleID)
21 {
22 TitleID = titleID;
23 }
24
25 public override string ToString()
26 {
27 if (Name != null)
28 return Name;
29 return $"{TitleID:X8} (Unknown)";
30 }
31 }
32
33 internal class TitleManager
34 {
35 // list from XLiveLessNess
36 // https://gitlab.com/GlitchyScripts/xlln-data/-/blob/main/XLiveLessNess/hub/titles.json?ref_type=heads
37 public static Dictionary<uint, string> TitleNameDatabase = new Dictionary<uint, string>()
38 {
39 { 0x33390FA0, "7 Wonders 3" },
40 { 0x33390FA1, "Chainz 2: Relinked" },
41 { 0x35530FA0, "Cubis Gold" },
42 { 0x35530FA1, "Cubis Gold 2" },
43 { 0x35530FA2, "Ranch Rush 2" },
44 { 0x355A0FA0, "Mahjongg Dimensions" },
45 { 0x36590FA0, "TextTwist 2" },
46 { 0x36590FA1, "Super TextTwist" },
47 { 0x41560829, "007: Quantum of Solace" },
48 { 0x41560FA0, "Call of Duty 4" },
49 { 0x41560FA1, "Call of Duty: WaW" },
50 { 0x41560FA2, "Singularity" },
51 { 0x41560FA3, "Transformers: WFC" },
52 { 0x41560FA4, "Blur" },
53 { 0x41560FA5, "[PROTOTYPE]" },
54 { 0x41560FA6, "007: Blood Stone" },
55 { 0x415807D5, "BlazBlue: Calamity Trigger" },
56 { 0x425307D6, "Fallout 3" },
57 { 0x42530FA0, "Hunted Demon's Forge" },
58 { 0x425607F3, "TRON: Evolution" },
59 { 0x42560FA0, "LEGO Pirates of the Caribbean: The Video Game" },
60 { 0x434307DE, "Lost Planet: Extreme Condition: Colonies Edition" },
61 { 0x434307F4, "Street Fighter IV" },
62 { 0x434307F7, "Resident Evil 5" },
63 { 0x43430803, "Dark Void" },
64 { 0x43430808, "Lost Planet 2" },
65 { 0x4343080E, "Dead Rising 2" },
66 { 0x43430FA0, "Super Street Fighter IV: Arcade Edition" },
67 { 0x43430FA1, "Resident Evil: Operation Raccoon City" },
68 { 0x43430FA2, "Dead Rising 2: Off the Record" },
69 { 0x43430FA5, "Street Fighter X Tekken" },
70 { 0x434D0820, "DiRT 2" },
71 { 0x434D082F, "FUEL" },
72 { 0x434D0831, "F1 2010" },
73 { 0x434D083E, "Operation Flashpoint: Red River" },
74 { 0x434D0FA0, "DiRT 3" },
75 { 0x434D0FA1, "F1 2011" },
76 { 0x44540FA0, "Crash Time 4: The Syndicate" },
77 { 0x44540FA1, "Crash Time 4 Demo" },
78 { 0x4541091C, "Dragon Age: Awakening" },
79 { 0x4541091F, "Battlefield: Bad Co. 2" },
80 { 0x45410920, "Mass Effect 2" },
81 { 0x45410921, "Dragon Age: Origins" },
82 { 0x45410935, "Bulletstorm" },
83 { 0x45410FA1, "Medal of Honor" },
84 { 0x45410FA2, "Need for Speed SHIFT" },
85 { 0x45410FA3, "Dead Space 2" },
86 { 0x45410FA4, "Bulletstrom DEMO" },
87 { 0x45410FA5, "Dragon Age 2" },
88 { 0x45410FA8, "Crysis 2" },
89 { 0x45410FAB, "The Sims 3" },
90 { 0x45410FAC, "The Sims 3 Late Night" },
91 { 0x45410FAD, "The Sims 3 Ambitions" },
92 { 0x45410FAE, "World Adventures" },
93 { 0x45410FAF, "The Sims Medieval" },
94 { 0x45410FB1, "Darkspore" },
95 { 0x45410FB2, "NFS SHIFT 2 Unleashed" },
96 { 0x45410FB3, "Spore" },
97 { 0x45410FB4, "The Sims 3 Generations" },
98 { 0x45410FB5, "Alice: Madness Returns" },
99 { 0x45410FB6, "Harry Potter (DH2)" },
100 { 0x45410FB7, "The Sims Medieval Pirates & Nobles" },
101 { 0x45410FB8, "Tiger Woods PGA TOUR 12: The Masters" },
102 { 0x454D07D4, "Flatout Ultimate Carnage" },
103 { 0x46450FA0, "Divinity II - DKS" },
104 { 0x46450FA1, "Cities XL 2011" },
105 { 0x46450FA2, "The Next Big Thing" },
106 { 0x46450FA3, "Faery" },
107 { 0x46450FA4, "Pro Cycling Manager" },
108 { 0x46550FA0, "Jewel Quest 5" },
109 { 0x46550FA1, "Family Feud Dream Home" },
110 { 0x48450FA0, "AFL Live" },
111 { 0x48450FA1, "Rugby League Live 2" },
112 { 0x49470FA1, "Test Drive Ferrari Racing Legend" },
113 { 0x4B590FA0, "Tropico 3 Gold Edition" },
114 { 0x4B590FA1, "Patrician IV" },
115 { 0x4B590FA3, "Commandos Complete" },
116 { 0x4B590FA5, "Dungeons" },
117 { 0x4B590FA8, "Patrician: RoaD" },
118 { 0x4B590FA9, "Elements of War" },
119 { 0x4B590FAA, "The First Templar" },
120 { 0x4C4107EB, "Star Wars: The Clone Wars - Republic Heroes" },
121 { 0x4D5307D6, "Shadowrun" },
122 { 0x4D53080F, "Halo 2 Vista" },
123 { 0x4D530841, "Viva Piñata" },
124 { 0x4D530842, "Gears of War" },
125 { 0x4D5308D2, "Microsoft Flight" },
126 { 0x4D5308D3, "Firebird Project" },
127 { 0x4D530901, "Game Room" },
128 { 0x4D53090A, "Fable 3" },
129 { 0x4D530935, "Flight Simulator X" },
130 { 0x4D530936, "Age of Empires III" },
131 { 0x4D530937, "Fable: TLC" },
132 { 0x4D530942, "AoE Online - Beta" },
133 { 0x4D530FA0, "Zoo Tycoon 2" },
134 { 0x4D530FA2, "Toy Soldiers" },
135 { 0x4D530FA3, "Age of Empires Online" },
136 { 0x4D530FA4, "Toy Soldiers: Cold War" },
137 { 0x4D530FA5, "Ms. Splosion Man" },
138 { 0x4D530FA6, "Skulls of the Shogun" },
139 { 0x4D530FA7, "Insanely Twisted Shadow Planet" },
140 { 0x4D530FA8, "Iron Brigade" },
141 { 0x4D530FA9, "MGS Pinball FX2 GFWL Games For Windows Live" },
142 { 0x4D530FAA, "MGS Vodka PC" },
143 { 0x4D5388B0, "BugBash 2" },
144 { 0x4E4D0FA1, "Dark Souls: Prepare to Die Edition" },
145 { 0x4E4D0FA2, "ACE COMBAT ASSAULT HORIZON: Enhanced Edition" },
146 { 0x4E4E0FA0, "Trainz Simulator 2010" },
147 { 0x4E4E0FA1, "Settle and Carlisle" },
148 { 0x4E4E0FA2, "Classic Cabon City" },
149 { 0x4E4E0FA3, "TS 2010: Blue Comet" },
150 { 0x4E4E0FA4, "Trainz Simulator 12" },
151 { 0x4F420FA0, "BubbleTown" },
152 { 0x4F430FA0, "King's Bounty Platinum" },
153 { 0x50470FA1, "Bejeweled 2" },
154 { 0x50470FA3, "Bookworm" },
155 { 0x50470FA4, "Plants vs. Zombies" },
156 { 0x50470FA5, "Zuma's Revenge" },
157 { 0x50470FA6, "Bejeweled 3" },
158 { 0x50580FA0, "Europa Universalis III" },
159 { 0x50580FA1, "Hearts of Iron III" },
160 { 0x50580FA2, "King Arthur" },
161 { 0x50580FA3, "Mount & Blade Warband" },
162 { 0x50580FA4, "Victoria 2" },
163 { 0x50580FA6, "EU 3: Divine Wind" },
164 { 0x50580FA7, "EU3:Heir to the Throne" },
165 { 0x50580FA8, "King Arthur The Druids" },
166 { 0x50580FA9, "King Arthur The Saxons" },
167 { 0x50580FAB, "Cities in Motion" },
168 { 0x50580FAC, "Cities in Motion" },
169 { 0x50580FAD, "EU 3: Chronicles" },
170 { 0x50580FAE, "Darkest Hour" },
171 { 0x50580FAF, "MnB: With Fire & Sword" },
172 { 0x50580FB0, "King Arthur Collection" },
173 { 0x50580FB1, "Supreme Ruler Cold War" },
174 { 0x50580FB2, "Pirates of Black Cove" },
175 { 0x51320FA0, "Poker Superstars III" },
176 { 0x51320FA1, "Slingo Deluxe" },
177 { 0x534307EB, "Kane & Lynch: Dead Men" },
178 { 0x534307FA, "Battlestations: Pacific" },
179 { 0x534307FF, "Batman: Arkham Asylum" },
180 { 0x53430800, "Battlestations: Pacific" },
181 { 0x5343080C, "Batman: AA GOTY" },
182 { 0x53430813, "ChampionshipManager 10" },
183 { 0x53430814, "Tomb Raider Underworld" },
184 { 0x534507F0, "Universe at War: Earth Assault" },
185 { 0x534507F6, "The Club" },
186 { 0x53450826, "Stormrise" },
187 { 0x5345082C, "Vancouver 2010" },
188 { 0x53450849, "Alpha Protocol" },
189 { 0x5345084E, "Football Manager 2010" },
190 { 0x53450854, "Rome: Total War" },
191 { 0x53450FA0, "Football Manager 2011" },
192 { 0x53450FA1, "Dreamcast Collection" },
193 { 0x53450FA2, "Virtua Tennis 4" },
194 { 0x53460FA0, "A Vampyre Story" },
195 { 0x53460FA1, "Ankh 2" },
196 { 0x53460FA2, "Ankh 3" },
197 { 0x53460FA3, "Rise of Flight: ICE" },
198 { 0x535007E3, "Section 8" },
199 { 0x53510FA0, "Deus Ex: GotY" },
200 { 0x53510FA1, "Deus Ex: Invisible War" },
201 { 0x53510FA2, "Hitman: Blood Money" },
202 { 0x53510FA3, "Thief: Deadly Shadows" },
203 { 0x53510FA4, "Hitman 2: SA" },
204 { 0x53510FA5, "MINI NINJAS" },
205 { 0x53510FA6, "Tomb Raider:Legend" },
206 { 0x53510FA7, "Tomb Raider: Anniv." },
207 { 0x53510FA8, "Battlestations: Midway" },
208 { 0x53510FA9, "Conflict: Denied Ops" },
209 { 0x53510FAA, "Project: Snowblind" },
210 { 0x544707D4, "Section 8: Prejudice" },
211 { 0x5451081F, "Juiced 2: Hot Import Nights" },
212 { 0x5451082D, "Warhammer 40,000: Dawn of War 2: Chaos Rising" },
213 { 0x54510837, "Red Faction: Guerrilla" },
214 { 0x54510868, "DoW II: Chaos Rising" },
215 { 0x54510871, "Saints Row 2" },
216 { 0x54510872, "S.T.A.L.K.E.R." },
217 { 0x5451087F, "Dawn of War" },
218 { 0x54510880, "DoW - Dark Crusade" },
219 { 0x54510881, "Supreme Commander" },
220 { 0x54510882, "Supreme Commander: Forged Alliance" },
221 { 0x5454083B, "Grand Theft Auto IV" },
222 { 0x5454085C, "BioShock 2" },
223 { 0x5454086E, "GTA IV: EFLC" },
224 { 0x54540873, "Borderlands" },
225 { 0x54540874, "Civ IV: Complete" },
226 { 0x54540876, "GTA San Andreas" },
227 { 0x54540877, "GTA: Vice City" },
228 { 0x54540878, "Max Payne 2" },
229 { 0x54540879, "Max Payne" },
230 { 0x5454087B, "BioShock" },
231 { 0x54540880, "Bully Scholarship Ed." },
232 { 0x54540881, "Grand Theft Auto III" },
233 { 0x54590FA0, "RIFT" },
234 { 0x54590FA1, "RIFT CE" },
235 { 0x54590FA2, "RIFT AoH CE" },
236 { 0x554C0FA0, "4 Elements" },
237 { 0x554C0FA1, "Gardenscapes" },
238 { 0x554C0FA2, "Call of Atlantis" },
239 { 0x554C0FA3, "Around the World in 80" },
240 { 0x554C0FA4, "Fishdom: Spooky Splash" },
241 { 0x55530855, "Prince of Persia: TFS" },
242 { 0x55530856, "Assassin's Creed II" },
243 { 0x55530857, "SplinterCellConviction" },
244 { 0x55530859, "Prince of Persia: WW" },
245 { 0x5553085A, "Prince of Persia: SoT" },
246 { 0x5553085B, "The Settlers 7" },
247 { 0x5553085E, "Assassin's Creed" },
248 { 0x5553085F, "World In Conflict" },
249 { 0x55530860, "Dawn of Discovery Gold" },
250 { 0x55530861, "Prince of Persia" },
251 { 0x55530862, "TC's RainbowSix Vegas2" },
252 { 0x55530864, "GRAW 2" },
253 { 0x55530865, "Far Cry 2" },
254 { 0x55530866, "Silent Hunter 5" },
255 { 0x55530FA0, "Prince of Persia: TT" },
256 { 0x55530FA1, "Tom Clancy's H.A.W.X.2" },
257 { 0x55530FA2, "Shaun White Skate" },
258 { 0x55530FA3, "AC Brotherhood" },
259 { 0x55530FA4, "AC Brotherhood Deluxe" },
260 { 0x55530FA6, "From Dust" },
261 { 0x57520806, "F.E.A.R. 2" },
262 { 0x57520808, "LEGO Batman" },
263 { 0x57520809, "LEGO Harry Potter: Years 1-4" },
264 { 0x57520FA0, "Batman: Arkham City" },
265 { 0x57520FA1, "LEGO Universe" },
266 { 0x57520FA2, "Mortal Kombat Arcade Kollection" },
267 { 0x57520FA3, "Gotham City Impostors" },
268 { 0x584109EB, "Tinker" },
269 { 0x584109F0, "World of Goo" },
270 { 0x584109F1, "Mahjong Wisdom" },
271 { 0x58410A01, "Where's Waldo" },
272 { 0x58410A10, "Osmos" },
273 { 0x58410A1C, "Carneyvale Showtime" },
274 { 0x58410A6D, "Blacklight: Tango Down" },
275 { 0x585207D1, "XLive" },
276 { 0x5A450FA0, "Battle vs. Chess" },
277 { 0x5A450FA1, "Two Worlds II" },
278 { 0x5A500FA1, "Kona's Crate" }
279 };
280
281 public static Dictionary<uint, KnownTitle> KnownTitles = new Dictionary<uint, KnownTitle>();
282
283 public static void FoundTitleExists(uint titleID)
284 {
285 if (!KnownTitles.ContainsKey(titleID))
286 KnownTitles[titleID] = new KnownTitle(titleID);
287 if (TitleNameDatabase.ContainsKey(titleID))
288 KnownTitles[titleID].Name = TitleNameDatabase[titleID];
289 }
290
291 public static void FoundTitleName(uint titleID, string name)
292 {
293 FoundTitleExists(titleID);
294 KnownTitles[titleID].Name = name;
295 }
296
297 public static string GetTitleProductKey(uint titleID)
298 {
299 if (KnownTitles.ContainsKey(titleID))
300 {
301 // if we already fetched the product key, don't try to fetch it again
302 if (KnownTitles[titleID].ProductKey != null)
303 return KnownTitles[titleID].ProductKey;
304
305 string rawTokenPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
306 $"Microsoft\\Xlive\\Titles\\{titleID:X8}\\Token.bin");
307 if (File.Exists(rawTokenPath)) {
308 byte[] tokenBytes = File.ReadAllBytes(rawTokenPath);
309 // first 32-bits are a size, we can ignore that
310 byte[] rawTokenBytes = tokenBytes.Skip(4).ToArray();
311 // try to decrypt it, if we fail just assume the key is a dud
312 try
313 {
314 byte[] unprotectedBytes = ProtectedData.Unprotect(rawTokenBytes, null, DataProtectionScope.CurrentUser);
315 string productKey = Encoding.UTF8.GetString(unprotectedBytes);
316 KnownTitles[titleID].ProductKey = productKey;
317 return productKey;
318 } catch (Exception)
319 {
320 return null;
321 }
322 }
323 }
324 return null;
325 }
326
327 public static byte[] GetConfigSector(uint titleID, int sectorID)
328 {
329 if (KnownTitles.ContainsKey(titleID))
330 {
331 string configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
332 $"Microsoft\\Xlive\\Titles\\{titleID:X8}\\config.bin");
333 byte[] configData = null;
334 if (KnownTitles[titleID].ConfigData == null)
335 {
336 if (File.Exists(configPath))
337 KnownTitles[titleID].ConfigData = File.ReadAllBytes(configPath);
338 else
339 return null;
340 }
341 configData = KnownTitles[titleID].ConfigData;
342
343 int sectorStart = sectorID * 0x400;
344 // make sure the sector isn't empty
345 if (configData[sectorStart] != 0x00)
346 {
347 // copy it into a buffer and unprotect it
348 byte[] sector = new byte[0x400];
349 Buffer.BlockCopy(configData, sectorStart, sector, 0, 0x400);
350 // try to decrypt it, if we fail just assume the sector doesn't exist
351 try
352 {
353 byte[] unprotectedSector = ProtectedData.Unprotect(sector, null, DataProtectionScope.CurrentUser);
354 return unprotectedSector;
355 } catch (Exception)
356 {
357 return null;
358 }
359 } else
360 {
361 return null;
362 }
363 }
364 return null;
365 }
366
367 public static void WriteConfigSector(uint titleID, int sectorID, byte[] data)
368 {
369 byte[] sector = new byte[0x1EC];
370 Buffer.BlockCopy(data, 0, sector, 0, data.Length);
371
372 byte[] protectedSector = new byte[0x400];
373 byte[] protectedSectorBytes = ProtectedData.Protect(sector, null, DataProtectionScope.CurrentUser);
374 Buffer.BlockCopy(protectedSectorBytes, 0, protectedSector, 0, protectedSectorBytes.Length);
375
376 string configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
377 $"Microsoft\\Xlive\\Titles\\{titleID:X8}\\config.bin");
378 byte[] configData = null;
379 if (File.Exists(configPath))
380 configData = File.ReadAllBytes(configPath);
381 else
382 configData = new byte[0x5000];
383 Buffer.BlockCopy(protectedSector, 0, configData, sectorID * 0x400, protectedSector.Length);
384 File.WriteAllBytes(configPath, configData);
385 }
386 }
387}