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