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