gameux: Improve stub for GameExplorerImpl_VerifyAccess.
[wine] / dlls / gameux / gameexplorer.c
1 /*
2  *    Gameux library coclass GameExplorer implementation
3  *
4  * Copyright (C) 2010 Mariusz PluciƄski
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #define COBJMACROS
21
22 #include "config.h"
23
24 #include "ole2.h"
25 #include "sddl.h"
26 #include "xmldom.h"
27
28 #include "gameux.h"
29 #include "gameux_private.h"
30
31 #include "initguid.h"
32 #include "msxml2.h"
33
34 #include "wine/debug.h"
35 #include "winreg.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
38
39 /* function from Shell32, not defined in header */
40 extern BOOL WINAPI GUIDFromStringW(LPCWSTR psz, LPGUID pguid);
41
42 /*******************************************************************************
43  * GameUX helper functions
44  */
45 /*******************************************************************************
46  * GAMEUX_initGameData
47  *
48  * Internal helper function. Description available in gameux_private.h file
49  */
50 void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
51 {
52     GameData->sGDFBinaryPath = NULL;
53     GameData->sGameInstallDirectory = NULL;
54     GameData->bstrName = NULL;
55     GameData->bstrDescription = NULL;
56 }
57 /*******************************************************************************
58  * GAMEUX_uninitGameData
59  *
60  * Internal helper function. Description available in gameux_private.h file
61  */
62 void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
63 {
64     HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
65     HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
66     SysFreeString(GameData->bstrName);
67     SysFreeString(GameData->bstrDescription);
68 }
69 /*******************************************************************************
70  * GAMEUX_buildGameRegistryPath
71  *
72  * Internal helper function. Description available in gameux_private.h file
73  */
74 HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
75         LPCGUID gameInstanceId,
76         LPWSTR* lpRegistryPath)
77 {
78     static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
79             'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
80             'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
81     static const WCHAR sGames[] = {'G','a','m','e','s',0};
82     static const WCHAR sBackslash[] = {'\\',0};
83
84     HRESULT hr = S_OK;
85     HANDLE hToken = NULL;
86     PTOKEN_USER pTokenUser = NULL;
87     DWORD dwLength;
88     LPWSTR lpSID = NULL;
89     WCHAR sInstanceId[40];
90     WCHAR sRegistryPath[8192];
91
92     TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
93
94     /* this will make freeing it easier for user */
95     *lpRegistryPath = NULL;
96
97     lstrcpyW(sRegistryPath, sGameUxRegistryPath);
98     lstrcatW(sRegistryPath, sBackslash);
99
100     if(installScope == GIS_CURRENT_USER)
101     {
102         /* build registry path containing user's SID */
103         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
104             hr = HRESULT_FROM_WIN32(GetLastError());
105
106         if(SUCCEEDED(hr))
107         {
108             if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
109                     GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
110                 hr = HRESULT_FROM_WIN32(GetLastError());
111
112             if(SUCCEEDED(hr))
113             {
114                 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
115                 if(!pTokenUser)
116                     hr = E_OUTOFMEMORY;
117             }
118
119             if(SUCCEEDED(hr))
120                 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
121                     hr = HRESULT_FROM_WIN32(GetLastError());
122
123             if(SUCCEEDED(hr))
124                 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
125                     hr = HRESULT_FROM_WIN32(GetLastError());
126
127             if(SUCCEEDED(hr))
128             {
129                 lstrcatW(sRegistryPath, lpSID);
130                 LocalFree(lpSID);
131             }
132
133             HeapFree(GetProcessHeap(), 0, pTokenUser);
134             CloseHandle(hToken);
135         }
136     }
137     else if(installScope == GIS_ALL_USERS)
138         /* build registry path without SID */
139         lstrcatW(sRegistryPath, sGames);
140     else
141         hr = E_INVALIDARG;
142
143     /* put game's instance id on the end of path, only if instance id was given */
144     if(gameInstanceId)
145     {
146         if(SUCCEEDED(hr))
147             hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
148
149         if(SUCCEEDED(hr))
150         {
151             lstrcatW(sRegistryPath, sBackslash);
152             lstrcatW(sRegistryPath, sInstanceId);
153         }
154     }
155
156     if(SUCCEEDED(hr))
157     {
158         *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
159         if(!*lpRegistryPath)
160             hr = E_OUTOFMEMORY;
161     }
162
163     if(SUCCEEDED(hr))
164         lstrcpyW(*lpRegistryPath, sRegistryPath);
165
166     TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
167     return hr;
168 }
169 /*******************************************************************************
170  * GAMEUX_WriteRegistryRecord
171  *
172  * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
173  * structure) into expected place in registry.
174  *
175  * Parameters:
176  *  GameData                            [I]     structure with data which will
177  *                                              be written into registry.
178  *                                              Proper values of fields installScope
179  *                                              and guidInstanceId are required
180  *                                              to create registry key.
181  *
182  * Schema of naming registry keys associated with games is available in
183  * description of _buildGameRegistryPath internal function.
184  *
185  * List of registry keys associated with structure fields:
186  *  Key                              Field in GAMEUX_GAME_DATA structure
187  *   ApplicationId                    guidApplicationId
188  *   ConfigApplicationPath            sGameInstallDirectory
189  *   ConfigGDFBinaryPath              sGDFBinaryPath
190  *   Title                            bstrName
191  *
192  */
193 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
194 {
195     static const WCHAR sApplicationId[] =
196             {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
197     static const WCHAR sConfigApplicationPath[] =
198             {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
199     static const WCHAR sConfigGDFBinaryPath[] =
200             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
201     static const WCHAR sTitle[] =
202             {'T','i','t','l','e',0};
203     static const WCHAR sDescription[] =
204             {'D','e','s','c','r','i','p','t','i','o','n',0};
205
206     HRESULT hr, hr2;
207     LPWSTR lpRegistryKey;
208     HKEY hKey;
209     WCHAR sGameApplicationId[40];
210
211     TRACE("(%p)\n", GameData);
212
213     hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
214
215     if(SUCCEEDED(hr))
216         hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
217
218     if(SUCCEEDED(hr))
219         hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
220                                                 0, NULL, 0, KEY_ALL_ACCESS, NULL,
221                                                 &hKey, NULL));
222
223     if(SUCCEEDED(hr))
224     {
225         /* write game data to registry key */
226         hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
227                                                REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
228                                                (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
229
230         if(SUCCEEDED(hr))
231             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
232                                                    REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
233                                                    (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
234
235         if(SUCCEEDED(hr))
236             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
237                                                    REG_SZ, (LPBYTE)(sGameApplicationId),
238                                                    (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
239
240         if(SUCCEEDED(hr))
241             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
242                                                    REG_SZ, (LPBYTE)(GameData->bstrName),
243                                                    (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
244
245         if(SUCCEEDED(hr))
246             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0,
247                                                    REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName),
248                                                    (lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR)));
249
250         RegCloseKey(hKey);
251
252         if(FAILED(hr))
253         {
254             /* if something failed, remove whole key */
255             hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, 0, 0);
256             /* do not overwrite old failure code with new success code */
257             if(FAILED(hr2))
258                 hr = hr2;
259         }
260     }
261
262     HeapFree(GetProcessHeap(), 0, lpRegistryKey);
263     TRACE("returning 0x%x\n", hr);
264     return hr;
265 }
266 /*******************************************************************************
267  * GAMEUX_ProcessGameDefinitionElement
268  *
269  * Helper function, parses single element from Game Definition
270  *
271  * Parameters:
272  *  lpXMLElement                        [I]     game definition element
273  *  GameData                            [O]     structure, where parsed
274  *                                              data will be stored
275  */
276 static HRESULT GAMEUX_ProcessGameDefinitionElement(
277         IXMLDOMElement *element,
278         struct GAMEUX_GAME_DATA *GameData)
279 {
280     static const WCHAR sName[] =
281             {'N','a','m','e',0};
282     static const WCHAR sDescription[] =
283             {'D','e','s','c','r','i','p','t','i','o','n',0};
284
285     HRESULT hr;
286     BSTR bstrElementName;
287
288     TRACE("(%p, %p)\n", element, GameData);
289
290     hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
291     if(SUCCEEDED(hr))
292     {
293         /* check element name */
294         if(lstrcmpW(bstrElementName, sName) == 0)
295             hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
296
297         else if(lstrcmpW(bstrElementName, sDescription) == 0)
298             hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
299
300         else
301             FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
302
303         SysFreeString(bstrElementName);
304     }
305
306     return hr;
307 }
308 /*******************************************************************************
309  * GAMEUX_ParseGameDefinition
310  *
311  * Helper function, loads data from given XML element into fields of GAME_DATA
312  * structure
313  *
314  * Parameters:
315  *  lpXMLGameDefinitionElement          [I]     Game Definition XML element
316  *  GameData                            [O]     structure where data loaded from
317  *                                              XML element will be stored in
318  */
319 static HRESULT GAMEUX_ParseGameDefinition(
320         IXMLDOMElement *gdElement,
321         struct GAMEUX_GAME_DATA *GameData)
322 {
323     static const WCHAR sGameId[] = {'g','a','m','e','I','D',0};
324
325     HRESULT hr = S_OK;
326     BSTR bstrAttribute;
327     VARIANT variant;
328     IXMLDOMNodeList *childrenList;
329     IXMLDOMNode *nextNode;
330     IXMLDOMElement *nextElement;
331
332     TRACE("(%p, %p)\n", gdElement, GameData);
333
334     bstrAttribute = SysAllocString(sGameId);
335     if(!bstrAttribute)
336         hr = E_OUTOFMEMORY;
337
338     hr = IXMLDOMElement_getAttribute(gdElement, bstrAttribute, &variant);
339
340     if(SUCCEEDED(hr))
341     {
342         hr = ( GUIDFromStringW(V_BSTR(&variant), &GameData->guidApplicationId)==TRUE ? S_OK : E_FAIL);
343
344         SysFreeString(V_BSTR(&variant));
345     }
346
347     SysFreeString(bstrAttribute);
348
349     /* browse subnodes */
350     if(SUCCEEDED(hr))
351         hr = IXMLDOMElement_get_childNodes(gdElement, &childrenList);
352
353     if(SUCCEEDED(hr))
354     {
355         do
356         {
357             hr = IXMLDOMNodeList_nextNode(childrenList, &nextNode);
358
359             if(hr == S_OK)
360             {
361                 hr = IXMLDOMNode_QueryInterface(nextNode, &IID_IXMLDOMElement,
362                                                 (LPVOID*)&nextElement);
363
364                 if(SUCCEEDED(hr))
365                 {
366                     hr = GAMEUX_ProcessGameDefinitionElement(nextElement, GameData);
367                     IXMLDOMElement_Release(nextElement);
368                 }
369
370                 IXMLDOMElement_Release(nextNode);
371             }
372         }
373         while(hr == S_OK);
374         hr = S_OK;
375
376         IXMLDOMNodeList_Release(childrenList);
377     }
378
379     return hr;
380 }
381 /*******************************************************************************
382  * GAMEUX_ParseGDFBinary
383  *
384  * Helper funtion, loads given binary and parses embed GDF if there's any.
385  *
386  * Parameters:
387  *  GameData                [I/O]   Structure with game's data. Content of field
388  *                                  sGDFBinaryPath defines path to binary, from
389  *                                  which embed GDF will be loaded. Data from
390  *                                  GDF will be stored in other fields of this
391  *                                  structure.
392  */
393 static HRESULT GAMEUX_ParseGDFBinary(struct GAMEUX_GAME_DATA *GameData)
394 {
395     static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
396     static const WCHAR sDATA[] = {'D','A','T','A',0};
397     static const WCHAR sSlash[] = {'/',0};
398
399     HRESULT hr = S_OK;
400     WCHAR sResourcePath[MAX_PATH];
401     VARIANT variant;
402     VARIANT_BOOL isSuccessful;
403     IXMLDOMDocument *document;
404     IXMLDOMNode *gdNode;
405     IXMLDOMElement *root, *gdElement;
406
407     TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
408
409     /* prepare path to GDF, using res:// prefix */
410     lstrcpyW(sResourcePath, sRes);
411     lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
412     lstrcatW(sResourcePath, sSlash);
413     lstrcatW(sResourcePath, sDATA);
414     lstrcatW(sResourcePath, sSlash);
415     lstrcatW(sResourcePath, ID_GDF_XML_STR);
416
417     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
418             &IID_IXMLDOMDocument, (void**)&document);
419
420     if(SUCCEEDED(hr))
421     {
422         /* load GDF into MSXML */
423         V_VT(&variant) = VT_BSTR;
424         V_BSTR(&variant) = SysAllocString(sResourcePath);
425         if(!V_BSTR(&variant))
426             hr = E_OUTOFMEMORY;
427
428         if(SUCCEEDED(hr))
429         {
430             hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
431             if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
432                 hr = E_FAIL;
433         }
434
435         SysFreeString(V_BSTR(&variant));
436
437         if(SUCCEEDED(hr))
438         {
439             hr = IXMLDOMDocument_get_documentElement(document, &root);
440             if(hr == S_FALSE)
441                 hr = E_FAIL;
442         }
443
444         if(SUCCEEDED(hr))
445         {
446             hr = IXMLDOMElement_get_firstChild(root, &gdNode);
447             if(hr == S_FALSE)
448                 hr = E_FAIL;
449
450             if(SUCCEEDED(hr))
451             {
452                 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
453                 if(SUCCEEDED(hr))
454                 {
455                     hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
456                     IXMLDOMElement_Release(gdElement);
457                 }
458
459                 IXMLDOMNode_Release(gdNode);
460             }
461
462             IXMLDOMElement_Release(root);
463         }
464
465         IXMLDOMDocument_Release(document);
466     }
467
468     return hr;
469 }
470 /*******************************************************************
471  * GAMEUX_RemoveRegistryRecord
472  *
473  * Helper function, removes registry key associated with given game instance
474  */
475 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
476 {
477     HRESULT hr;
478     LPWSTR lpRegistryPath = NULL;
479     TRACE("(%s)\n", debugstr_guid(pInstanceID));
480
481     /* first, check is game installed for all users */
482     hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
483     if(SUCCEEDED(hr))
484         hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, 0, 0));
485
486     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
487
488     /* if not, check current user */
489     if(FAILED(hr))
490     {
491         hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
492         if(SUCCEEDED(hr))
493             hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, 0, 0));
494
495         HeapFree(GetProcessHeap(), 0, lpRegistryPath);
496     }
497
498     return hr;
499 }
500 /*******************************************************************************
501  * GAMEUX_RegisterGame
502  *
503  * Internal helper function. Description available in gameux_private.h file
504  */
505 HRESULT WINAPI GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
506         LPCWSTR sGameInstallDirectory,
507         GAME_INSTALL_SCOPE installScope,
508         GUID *pInstanceID)
509 {
510     HRESULT hr = S_OK;
511     struct GAMEUX_GAME_DATA GameData;
512
513     TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
514
515     GAMEUX_initGameData(&GameData);
516     GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
517     lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
518     GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
519     lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
520     GameData.installScope = installScope;
521
522     /* generate GUID if it was not provided by user */
523     if(IsEqualGUID(pInstanceID, &GUID_NULL))
524         hr = CoCreateGuid(pInstanceID);
525
526     GameData.guidInstanceId = *pInstanceID;
527
528     /* load data from GDF binary */
529     if(SUCCEEDED(hr))
530         hr = GAMEUX_ParseGDFBinary(&GameData);
531
532     /* save data to registry */
533     if(SUCCEEDED(hr))
534         hr = GAMEUX_WriteRegistryRecord(&GameData);
535
536     GAMEUX_uninitGameData(&GameData);
537     TRACE("returing 0x%08x\n", hr);
538     return hr;
539 }
540 /*******************************************************************************
541  * GAMEUX_IsGameKeyExist
542  *
543  * Helper function, checks if game's registry ath exists in given scope
544  *
545  * Parameters:
546  *  installScope            [I]     scope to search game in
547  *  InstanceID              [I]     game instance identifier
548  *  lpRegistryPath          [O]     place to store address of registry path to
549  *                                  the game. It is filled only if key exists.
550  *                                  It must be freed by HeapFree(GetProcessHeap(), 0, ...)
551  *
552  * Returns:
553  *  S_OK                key was found properly
554  *  S_FALSE             key does not exists
555  *
556  */
557 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
558     LPCGUID InstanceID,
559     LPWSTR* lpRegistryPath) {
560
561     HRESULT hr;
562     HKEY hKey;
563
564     hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
565
566     if(SUCCEEDED(hr))
567         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
568                                               0, KEY_WOW64_64KEY, &hKey));
569
570     if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
571         hr = S_FALSE;
572
573     if(hr == S_OK)
574         RegCloseKey(hKey);
575     else
576     {
577         /* if key does not exist or other error occured, do not return the path */
578         HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
579         *lpRegistryPath = NULL;
580     }
581
582     return hr;
583 }
584 /*******************************************************************************
585  * GAMEUX_LoadRegistryString
586  *
587  * Helper function, loads string from registry value and allocates buffer for it
588  */
589 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
590         LPCWSTR lpRegistryKey,
591         LPCWSTR lpRegistryValue,
592         LPWSTR* lpValue)
593 {
594     HRESULT hr;
595     DWORD dwSize;
596
597     *lpValue = NULL;
598
599     hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
600             RRF_RT_REG_SZ, NULL, NULL, &dwSize));
601
602     if(SUCCEEDED(hr))
603     {
604         *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
605         if(!*lpValue)
606             hr = E_OUTOFMEMORY;
607     }
608
609     if(SUCCEEDED(hr))
610         hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
611                 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
612
613     return hr;
614 }
615 /*******************************************************************************
616  * GAMEUX_UpdateGame
617  *
618  * Helper function, updates stored data about game with given InstanceID
619  */
620 static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
621     static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
622     static const WCHAR sConfigApplicationPath[] = {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
623
624     HRESULT hr;
625     GAME_INSTALL_SCOPE installScope;
626     LPWSTR lpRegistryPath;
627     LPWSTR lpGDFBinaryPath, lpGameInstallDirectory;
628
629     TRACE("(%p)\n", debugstr_guid(InstanceID));
630
631     /* first, check is game exists in CURRENT_USER scope  */
632     installScope = GIS_CURRENT_USER;
633     hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
634
635     if(hr == S_FALSE)
636     {
637         /* game not found in CURRENT_USER scope, let's check in ALL_USERS */
638         installScope = GIS_ALL_USERS;
639         hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
640     }
641
642     if(hr == S_FALSE)
643         /* still not found? let's inform user that game does not exists */
644         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
645
646     if(SUCCEEDED(hr))
647     {
648         /* game found, it's registry path is in lpRegistryPath and install
649          * scope in installScope */
650         TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
651
652         /* first, read required data about game */
653         hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
654             sConfigGDFBinaryPath, &lpGDFBinaryPath);
655
656         if(SUCCEEDED(hr))
657             hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
658                 sConfigApplicationPath, &lpGameInstallDirectory);
659
660         /* now remove currently existing registry key */
661         if(SUCCEEDED(hr))
662             hr = GAMEUX_RemoveRegistryRecord(InstanceID);
663
664         /* and add it again, it will cause in reparsing of whole GDF */
665         if(SUCCEEDED(hr))
666             hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
667                                      installScope, InstanceID);
668
669         HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
670         HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
671     }
672
673     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
674     TRACE("returning 0x%x\n", hr);
675     return hr;
676 }
677 /*******************************************************************************
678  * GAMEUX_FindGameInstanceId
679  *
680  * Internal helper function. Description available in gameux_private.h file
681  */
682 HRESULT GAMEUX_FindGameInstanceId(
683         LPCWSTR sGDFBinaryPath,
684         GAME_INSTALL_SCOPE installScope,
685         GUID* pInstanceId)
686 {
687     static const WCHAR sConfigGDFBinaryPath[] =
688             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
689
690     HRESULT hr;
691     BOOL found = FALSE;
692     LPWSTR lpRegistryPath = NULL;
693     HKEY hRootKey;
694     DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
695     LPWSTR lpName = NULL, lpValue = NULL;
696
697     hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
698
699     if(SUCCEEDED(hr))
700         /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
701         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
702                 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
703
704     if(SUCCEEDED(hr))
705     {
706         hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
707                 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
708
709         if(SUCCEEDED(hr))
710         {
711             ++dwMaxSubKeyLen; /* for string terminator */
712             lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
713             if(!lpName) hr = E_OUTOFMEMORY;
714         }
715
716         if(SUCCEEDED(hr))
717         {
718             for(i=0; i<dwSubKeys && !found; ++i)
719             {
720                 dwSubKeyLen = dwMaxSubKeyLen;
721                 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
722                         NULL, NULL, NULL, NULL));
723
724                 if(SUCCEEDED(hr))
725                     hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
726                                              sConfigGDFBinaryPath, &lpValue);
727
728                 if(SUCCEEDED(hr))
729                     if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
730                     {
731                         /* key found, let's copy instance id and exit */
732                         hr = (GUIDFromStringW(lpName, pInstanceId) ? S_OK : E_FAIL);
733                         found = TRUE;
734                     }
735                 HeapFree(GetProcessHeap(), 0, lpValue);
736             }
737         }
738
739         HeapFree(GetProcessHeap(), 0, lpName);
740         RegCloseKey(hRootKey);
741     }
742
743     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
744
745     if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
746         hr = S_FALSE;
747
748     return hr;
749 }
750 /*******************************************************************************
751  * GameExplorer implementation
752  */
753
754 typedef struct _GameExplorerImpl
755 {
756     const struct IGameExplorerVtbl *lpGameExplorerVtbl;
757     const struct IGameExplorer2Vtbl *lpGameExplorer2Vtbl;
758     LONG ref;
759 } GameExplorerImpl;
760
761 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
762 {
763     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorerVtbl));
764 }
765
766 static inline IGameExplorer* IGameExplorer_from_impl(GameExplorerImpl* This)
767 {
768     return (struct IGameExplorer*)&This->lpGameExplorerVtbl;
769 }
770
771 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
772 {
773     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorer2Vtbl));
774 }
775
776 static inline IGameExplorer2* IGameExplorer2_from_impl(GameExplorerImpl* This)
777 {
778     return (struct IGameExplorer2*)&This->lpGameExplorer2Vtbl;
779 }
780
781 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
782         IGameExplorer *iface,
783         REFIID riid,
784         void **ppvObject)
785 {
786     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
787
788     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
789
790     *ppvObject = NULL;
791
792     if(IsEqualGUID(riid, &IID_IUnknown) ||
793        IsEqualGUID(riid, &IID_IGameExplorer))
794     {
795         *ppvObject = IGameExplorer_from_impl(This);
796     }
797     else if(IsEqualGUID(riid, &IID_IGameExplorer2))
798     {
799         *ppvObject = IGameExplorer2_from_impl(This);
800     }
801     else
802     {
803         FIXME("interface %s not implemented\n", debugstr_guid(riid));
804         return E_NOINTERFACE;
805     }
806
807     IGameExplorer_AddRef(iface);
808     return S_OK;
809 }
810
811 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
812 {
813     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
814     LONG ref;
815
816     ref = InterlockedIncrement(&This->ref);
817
818     TRACE("(%p): ref=%d\n", This, ref);
819     return ref;
820 }
821
822 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
823 {
824     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
825     LONG ref;
826
827     ref = InterlockedDecrement(&This->ref);
828     TRACE("(%p): ref=%d\n", This, ref);
829
830     if(ref == 0)
831     {
832         TRACE("freeing GameExplorer object\n");
833         HeapFree(GetProcessHeap(), 0, This);
834     }
835
836     return ref;
837 }
838
839 static HRESULT WINAPI GameExplorerImpl_AddGame(
840         IGameExplorer *iface,
841         BSTR bstrGDFBinaryPath,
842         BSTR sGameInstallDirectory,
843         GAME_INSTALL_SCOPE installScope,
844         GUID *pInstanceID)
845 {
846     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
847     TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
848     return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
849 }
850
851 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
852         IGameExplorer *iface,
853         GUID instanceID)
854 {
855     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
856
857     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
858     return GAMEUX_RemoveRegistryRecord(&instanceID);
859 }
860
861 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
862         IGameExplorer *iface,
863         GUID instanceID)
864 {
865     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
866
867     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
868     return GAMEUX_UpdateGame(&instanceID);
869 }
870
871 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
872         IGameExplorer *iface,
873         BSTR sGDFBinaryPath,
874         BOOL *pHasAccess)
875 {
876     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
877
878     FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
879     *pHasAccess = TRUE;
880     return S_OK;
881 }
882
883 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
884 {
885     GameExplorerImpl_QueryInterface,
886     GameExplorerImpl_AddRef,
887     GameExplorerImpl_Release,
888     GameExplorerImpl_AddGame,
889     GameExplorerImpl_RemoveGame,
890     GameExplorerImpl_UpdateGame,
891     GameExplorerImpl_VerifyAccess
892 };
893
894
895 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
896         IGameExplorer2 *iface,
897         REFIID riid,
898         void **ppvObject)
899 {
900     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
901     return GameExplorerImpl_QueryInterface(IGameExplorer_from_impl(This), riid, ppvObject);
902 }
903
904 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
905 {
906     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
907     return GameExplorerImpl_AddRef(IGameExplorer_from_impl(This));
908 }
909
910 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
911 {
912     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
913     return GameExplorerImpl_Release(IGameExplorer_from_impl(This));
914 }
915
916 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
917         IGameExplorer2 *iface,
918         LPCWSTR binaryGDFPath,
919         BOOL *pHasAccess)
920 {
921     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
922     FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
923     return E_NOTIMPL;
924 }
925
926 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
927         IGameExplorer2 *iface,
928         LPCWSTR binaryGDFPath,
929         LPCWSTR installDirectory,
930         GAME_INSTALL_SCOPE installScope)
931 {
932     HRESULT hr;
933     GUID instanceId;
934     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
935
936     TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
937
938     if(!binaryGDFPath)
939         return E_INVALIDARG;
940
941     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
942
943     if(hr == S_FALSE)
944         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
945
946     if(hr == S_FALSE)
947     {
948         /* if game isn't yet registered, then install it */
949         instanceId = GUID_NULL;
950         hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
951     }
952     else if(hr == S_OK)
953         /* otherwise, update game */
954         hr = GAMEUX_UpdateGame(&instanceId);
955
956     return hr;
957 }
958
959 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
960         IGameExplorer2 *iface,
961         LPCWSTR binaryGDFPath)
962 {
963     HRESULT hr;
964     GUID instanceId;
965     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
966     TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
967
968     if(!binaryGDFPath)
969         return E_INVALIDARG;
970
971     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
972
973     if(hr == S_FALSE)
974         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
975
976     if(hr == S_OK)
977         hr = GAMEUX_RemoveRegistryRecord(&instanceId);
978
979     return hr;
980 }
981
982 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
983 {
984     GameExplorer2Impl_QueryInterface,
985     GameExplorer2Impl_AddRef,
986     GameExplorer2Impl_Release,
987     GameExplorer2Impl_InstallGame,
988     GameExplorer2Impl_UninstallGame,
989     GameExplorer2Impl_CheckAccess
990 };
991
992 /*
993  * Construction routine
994  */
995 HRESULT GameExplorer_create(
996         IUnknown* pUnkOuter,
997         IUnknown** ppObj)
998 {
999     GameExplorerImpl *pGameExplorer;
1000
1001     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1002
1003     pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1004
1005     if(!pGameExplorer)
1006         return E_OUTOFMEMORY;
1007
1008     pGameExplorer->lpGameExplorerVtbl = &GameExplorerImplVtbl;
1009     pGameExplorer->lpGameExplorer2Vtbl = &GameExplorer2ImplVtbl;
1010     pGameExplorer->ref = 1;
1011
1012     *ppObj = (IUnknown*)(&pGameExplorer->lpGameExplorerVtbl);
1013
1014     TRACE("returning iface: %p\n", *ppObj);
1015     return S_OK;
1016 }