po: Update French translation.
[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 struct parse_gdf_thread_param
392 {
393     struct GAMEUX_GAME_DATA *GameData;
394     HRESULT hr;
395 };
396
397 /*******************************************************************************
398  * GAMEUX_ParseGDFBinary
399  *
400  * Helper function, 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 DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param)
410 {
411     struct parse_gdf_thread_param *ctx = thread_param;
412     struct GAMEUX_GAME_DATA *GameData = ctx->GameData;
413     static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
414     static const WCHAR sDATA[] = {'D','A','T','A',0};
415     static const WCHAR sSlash[] = {'/',0};
416
417     HRESULT hr = S_OK;
418     WCHAR sResourcePath[MAX_PATH];
419     VARIANT variant;
420     VARIANT_BOOL isSuccessful;
421     IXMLDOMDocument *document;
422     IXMLDOMNode *gdNode;
423     IXMLDOMElement *root, *gdElement;
424
425     TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
426
427     /* prepare path to GDF, using res:// prefix */
428     lstrcpyW(sResourcePath, sRes);
429     lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
430     lstrcatW(sResourcePath, sSlash);
431     lstrcatW(sResourcePath, sDATA);
432     lstrcatW(sResourcePath, sSlash);
433     lstrcatW(sResourcePath, ID_GDF_XML_STR);
434
435     CoInitialize(NULL);
436
437     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
438             &IID_IXMLDOMDocument, (void**)&document);
439
440     if(SUCCEEDED(hr))
441     {
442         /* load GDF into MSXML */
443         V_VT(&variant) = VT_BSTR;
444         V_BSTR(&variant) = SysAllocString(sResourcePath);
445         if(!V_BSTR(&variant))
446             hr = E_OUTOFMEMORY;
447
448         if(SUCCEEDED(hr))
449         {
450             hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
451             if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
452                 hr = E_FAIL;
453         }
454
455         SysFreeString(V_BSTR(&variant));
456
457         if(SUCCEEDED(hr))
458         {
459             hr = IXMLDOMDocument_get_documentElement(document, &root);
460             if(hr == S_FALSE)
461                 hr = E_FAIL;
462         }
463
464         if(SUCCEEDED(hr))
465         {
466             hr = IXMLDOMElement_get_firstChild(root, &gdNode);
467             if(hr == S_FALSE)
468                 hr = E_FAIL;
469
470             if(SUCCEEDED(hr))
471             {
472                 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
473                 if(SUCCEEDED(hr))
474                 {
475                     hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
476                     IXMLDOMElement_Release(gdElement);
477                 }
478
479                 IXMLDOMNode_Release(gdNode);
480             }
481
482             IXMLDOMElement_Release(root);
483         }
484
485         IXMLDOMDocument_Release(document);
486     }
487
488     CoUninitialize();
489     ctx->hr = hr;
490     return 0;
491 }
492
493 /*******************************************************************
494  * GAMEUX_RemoveRegistryRecord
495  *
496  * Helper function, removes registry key associated with given game instance
497  */
498 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
499 {
500     HRESULT hr;
501     LPWSTR lpRegistryPath = NULL;
502     TRACE("(%s)\n", debugstr_guid(pInstanceID));
503
504     /* first, check is game installed for all users */
505     hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
506     if(SUCCEEDED(hr))
507         hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
508
509     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
510
511     /* if not, check current user */
512     if(FAILED(hr))
513     {
514         hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
515         if(SUCCEEDED(hr))
516             hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
517
518         HeapFree(GetProcessHeap(), 0, lpRegistryPath);
519     }
520
521     return hr;
522 }
523 /*******************************************************************************
524  *  GAMEUX_RegisterGame
525  *
526  * Internal helper function. Registers game associated with given GDF binary in
527  * Game Explorer. Implemented in gameexplorer.c
528  *
529  * Parameters:
530  *  sGDFBinaryPath                  [I]     path to binary containing GDF file in
531  *                                          resources
532  *  sGameInstallDirectory           [I]     path to directory, where game installed
533  *                                          it's files.
534  *  installScope                    [I]     scope of game installation
535  *  pInstanceID                     [I/O]   pointer to game instance identifier.
536  *                                          If pointing to GUID_NULL, then new
537  *                                          identifier will be generated automatically
538  *                                          and returned via this parameter
539  */
540 static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
541         LPCWSTR sGameInstallDirectory,
542         GAME_INSTALL_SCOPE installScope,
543         GUID *pInstanceID)
544 {
545     HRESULT hr = S_OK;
546     struct GAMEUX_GAME_DATA GameData;
547
548     TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
549
550     GAMEUX_initGameData(&GameData);
551     GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
552     lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
553     GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
554     lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
555     GameData.installScope = installScope;
556
557     /* generate GUID if it was not provided by user */
558     if(IsEqualGUID(pInstanceID, &GUID_NULL))
559         hr = CoCreateGuid(pInstanceID);
560
561     GameData.guidInstanceId = *pInstanceID;
562
563     /* load data from GDF binary */
564     if(SUCCEEDED(hr))
565     {
566         struct parse_gdf_thread_param thread_param;
567         HANDLE thread;
568         DWORD ret;
569
570         thread_param.GameData = &GameData;
571         if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret)))
572         {
573             ERR("Failed to create thread.\n");
574             hr = E_FAIL;
575             goto done;
576         }
577         ret = WaitForSingleObject(thread, INFINITE);
578         CloseHandle(thread);
579         if(ret != WAIT_OBJECT_0)
580         {
581             ERR("Wait failed (%#x).\n", ret);
582             hr = E_FAIL;
583             goto done;
584         }
585         hr = thread_param.hr;
586     }
587
588     /* save data to registry */
589     if(SUCCEEDED(hr))
590         hr = GAMEUX_WriteRegistryRecord(&GameData);
591
592 done:
593     GAMEUX_uninitGameData(&GameData);
594     TRACE("returning 0x%08x\n", hr);
595     return hr;
596 }
597 /*******************************************************************************
598  * GAMEUX_IsGameKeyExist
599  *
600  * Helper function, checks if game's registry ath exists in given scope
601  *
602  * Parameters:
603  *  installScope            [I]     scope to search game in
604  *  InstanceID              [I]     game instance identifier
605  *  lpRegistryPath          [O]     place to store address of registry path to
606  *                                  the game. It is filled only if key exists.
607  *                                  It must be freed by HeapFree(GetProcessHeap(), 0, ...)
608  *
609  * Returns:
610  *  S_OK                key was found properly
611  *  S_FALSE             key does not exists
612  *
613  */
614 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
615     LPCGUID InstanceID,
616     LPWSTR* lpRegistryPath) {
617
618     HRESULT hr;
619     HKEY hKey;
620
621     hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
622
623     if(SUCCEEDED(hr))
624         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
625                                               0, KEY_WOW64_64KEY, &hKey));
626
627     if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
628         hr = S_FALSE;
629
630     if(hr == S_OK)
631         RegCloseKey(hKey);
632     else
633     {
634         /* if the key does not exist or another error occurred, do not return the path */
635         HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
636         *lpRegistryPath = NULL;
637     }
638
639     return hr;
640 }
641 /*******************************************************************************
642  * GAMEUX_LoadRegistryString
643  *
644  * Helper function, loads string from registry value and allocates buffer for it
645  */
646 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
647         LPCWSTR lpRegistryKey,
648         LPCWSTR lpRegistryValue,
649         LPWSTR* lpValue)
650 {
651     HRESULT hr;
652     DWORD dwSize;
653
654     *lpValue = NULL;
655
656     hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
657             RRF_RT_REG_SZ, NULL, NULL, &dwSize));
658
659     if(SUCCEEDED(hr))
660     {
661         *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
662         if(!*lpValue)
663             hr = E_OUTOFMEMORY;
664     }
665
666     if(SUCCEEDED(hr))
667         hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
668                 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
669
670     return hr;
671 }
672 /*******************************************************************************
673  * GAMEUX_UpdateGame
674  *
675  * Helper function, updates stored data about game with given InstanceID
676  */
677 static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
678     static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
679     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};
680
681     HRESULT hr;
682     GAME_INSTALL_SCOPE installScope;
683     LPWSTR lpRegistryPath;
684     LPWSTR lpGDFBinaryPath, lpGameInstallDirectory;
685
686     TRACE("(%s)\n", debugstr_guid(InstanceID));
687
688     /* first, check is game exists in CURRENT_USER scope  */
689     installScope = GIS_CURRENT_USER;
690     hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
691
692     if(hr == S_FALSE)
693     {
694         /* game not found in CURRENT_USER scope, let's check in ALL_USERS */
695         installScope = GIS_ALL_USERS;
696         hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
697     }
698
699     if(hr == S_FALSE)
700         /* still not found? let's inform user that game does not exists */
701         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
702
703     if(SUCCEEDED(hr))
704     {
705         /* game found, it's registry path is in lpRegistryPath and install
706          * scope in installScope */
707         TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
708
709         /* first, read required data about game */
710         hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
711             sConfigGDFBinaryPath, &lpGDFBinaryPath);
712
713         if(SUCCEEDED(hr))
714             hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
715                 sConfigApplicationPath, &lpGameInstallDirectory);
716
717         /* now remove currently existing registry key */
718         if(SUCCEEDED(hr))
719             hr = GAMEUX_RemoveRegistryRecord(InstanceID);
720
721         /* and add it again, it will cause in reparsing of whole GDF */
722         if(SUCCEEDED(hr))
723             hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
724                                      installScope, InstanceID);
725
726         HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
727         HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
728     }
729
730     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
731     TRACE("returning 0x%x\n", hr);
732     return hr;
733 }
734 /*******************************************************************************
735  * GAMEUX_FindGameInstanceId
736  *
737  * Internal helper function. Description available in gameux_private.h file
738  */
739 HRESULT GAMEUX_FindGameInstanceId(
740         LPCWSTR sGDFBinaryPath,
741         GAME_INSTALL_SCOPE installScope,
742         GUID* pInstanceId)
743 {
744     static const WCHAR sConfigGDFBinaryPath[] =
745             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
746
747     HRESULT hr;
748     BOOL found = FALSE;
749     LPWSTR lpRegistryPath = NULL;
750     HKEY hRootKey;
751     DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
752     LPWSTR lpName = NULL, lpValue = NULL;
753
754     hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
755
756     if(SUCCEEDED(hr))
757         /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
758         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
759                 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
760
761     if(SUCCEEDED(hr))
762     {
763         hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
764                 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
765
766         if(SUCCEEDED(hr))
767         {
768             ++dwMaxSubKeyLen; /* for string terminator */
769             lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
770             if(!lpName) hr = E_OUTOFMEMORY;
771         }
772
773         if(SUCCEEDED(hr))
774         {
775             for(i=0; i<dwSubKeys && !found; ++i)
776             {
777                 dwSubKeyLen = dwMaxSubKeyLen;
778                 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
779                         NULL, NULL, NULL, NULL));
780
781                 if(SUCCEEDED(hr))
782                     hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
783                                              sConfigGDFBinaryPath, &lpValue);
784
785                 if(SUCCEEDED(hr))
786                     if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
787                     {
788                         /* key found, let's copy instance id and exit */
789                         hr = (GUIDFromStringW(lpName, pInstanceId) ? S_OK : E_FAIL);
790                         found = TRUE;
791                     }
792                 HeapFree(GetProcessHeap(), 0, lpValue);
793             }
794         }
795
796         HeapFree(GetProcessHeap(), 0, lpName);
797         RegCloseKey(hRootKey);
798     }
799
800     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
801
802     if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
803         hr = S_FALSE;
804
805     return hr;
806 }
807 /*******************************************************************************
808  * GameExplorer implementation
809  */
810
811 typedef struct _GameExplorerImpl
812 {
813     IGameExplorer IGameExplorer_iface;
814     IGameExplorer2 IGameExplorer2_iface;
815     LONG ref;
816 } GameExplorerImpl;
817
818 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
819 {
820     return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface);
821 }
822
823 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
824 {
825     return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface);
826 }
827
828 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
829         IGameExplorer *iface,
830         REFIID riid,
831         void **ppvObject)
832 {
833     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
834
835     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
836
837     *ppvObject = NULL;
838
839     if(IsEqualGUID(riid, &IID_IUnknown) ||
840        IsEqualGUID(riid, &IID_IGameExplorer))
841     {
842         *ppvObject = &This->IGameExplorer_iface;
843     }
844     else if(IsEqualGUID(riid, &IID_IGameExplorer2))
845     {
846         *ppvObject = &This->IGameExplorer2_iface;
847     }
848     else
849     {
850         FIXME("interface %s not implemented\n", debugstr_guid(riid));
851         return E_NOINTERFACE;
852     }
853
854     IGameExplorer_AddRef(iface);
855     return S_OK;
856 }
857
858 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
859 {
860     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
861     LONG ref;
862
863     ref = InterlockedIncrement(&This->ref);
864
865     TRACE("(%p): ref=%d\n", This, ref);
866     return ref;
867 }
868
869 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
870 {
871     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
872     LONG ref;
873
874     ref = InterlockedDecrement(&This->ref);
875     TRACE("(%p): ref=%d\n", This, ref);
876
877     if(ref == 0)
878     {
879         TRACE("freeing GameExplorer object\n");
880         HeapFree(GetProcessHeap(), 0, This);
881     }
882
883     return ref;
884 }
885
886 static HRESULT WINAPI GameExplorerImpl_AddGame(
887         IGameExplorer *iface,
888         BSTR bstrGDFBinaryPath,
889         BSTR sGameInstallDirectory,
890         GAME_INSTALL_SCOPE installScope,
891         GUID *pInstanceID)
892 {
893     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
894     TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
895     return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
896 }
897
898 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
899         IGameExplorer *iface,
900         GUID instanceID)
901 {
902     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
903
904     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
905     return GAMEUX_RemoveRegistryRecord(&instanceID);
906 }
907
908 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
909         IGameExplorer *iface,
910         GUID instanceID)
911 {
912     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
913
914     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
915     return GAMEUX_UpdateGame(&instanceID);
916 }
917
918 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
919         IGameExplorer *iface,
920         BSTR sGDFBinaryPath,
921         BOOL *pHasAccess)
922 {
923     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
924
925     FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
926     *pHasAccess = TRUE;
927     return S_OK;
928 }
929
930 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
931 {
932     GameExplorerImpl_QueryInterface,
933     GameExplorerImpl_AddRef,
934     GameExplorerImpl_Release,
935     GameExplorerImpl_AddGame,
936     GameExplorerImpl_RemoveGame,
937     GameExplorerImpl_UpdateGame,
938     GameExplorerImpl_VerifyAccess
939 };
940
941
942 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
943         IGameExplorer2 *iface,
944         REFIID riid,
945         void **ppvObject)
946 {
947     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
948     return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject);
949 }
950
951 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
952 {
953     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
954     return GameExplorerImpl_AddRef(&This->IGameExplorer_iface);
955 }
956
957 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
958 {
959     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
960     return GameExplorerImpl_Release(&This->IGameExplorer_iface);
961 }
962
963 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
964         IGameExplorer2 *iface,
965         LPCWSTR binaryGDFPath,
966         BOOL *pHasAccess)
967 {
968     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
969     FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
970     return E_NOTIMPL;
971 }
972
973 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
974         IGameExplorer2 *iface,
975         LPCWSTR binaryGDFPath,
976         LPCWSTR installDirectory,
977         GAME_INSTALL_SCOPE installScope)
978 {
979     HRESULT hr;
980     GUID instanceId;
981     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
982
983     TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
984
985     if(!binaryGDFPath)
986         return E_INVALIDARG;
987
988     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
989
990     if(hr == S_FALSE)
991         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
992
993     if(hr == S_FALSE)
994     {
995         /* if game isn't yet registered, then install it */
996         instanceId = GUID_NULL;
997         hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
998     }
999     else if(hr == S_OK)
1000         /* otherwise, update game */
1001         hr = GAMEUX_UpdateGame(&instanceId);
1002
1003     return hr;
1004 }
1005
1006 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
1007         IGameExplorer2 *iface,
1008         LPCWSTR binaryGDFPath)
1009 {
1010     HRESULT hr;
1011     GUID instanceId;
1012     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
1013     TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
1014
1015     if(!binaryGDFPath)
1016         return E_INVALIDARG;
1017
1018     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
1019
1020     if(hr == S_FALSE)
1021         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
1022
1023     if(hr == S_OK)
1024         hr = GAMEUX_RemoveRegistryRecord(&instanceId);
1025
1026     return hr;
1027 }
1028
1029 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
1030 {
1031     GameExplorer2Impl_QueryInterface,
1032     GameExplorer2Impl_AddRef,
1033     GameExplorer2Impl_Release,
1034     GameExplorer2Impl_InstallGame,
1035     GameExplorer2Impl_UninstallGame,
1036     GameExplorer2Impl_CheckAccess
1037 };
1038
1039 /*
1040  * Construction routine
1041  */
1042 HRESULT GameExplorer_create(
1043         IUnknown* pUnkOuter,
1044         IUnknown** ppObj)
1045 {
1046     GameExplorerImpl *pGameExplorer;
1047
1048     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1049
1050     pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1051
1052     if(!pGameExplorer)
1053         return E_OUTOFMEMORY;
1054
1055     pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl;
1056     pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl;
1057     pGameExplorer->ref = 1;
1058
1059     *ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface;
1060
1061     TRACE("returning iface: %p\n", *ppObj);
1062     return S_OK;
1063 }