gameux: Add storing Title registry value.
[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 }
56 /*******************************************************************************
57  * GAMEUX_uninitGameData
58  *
59  * Internal helper function. Description available in gameux_private.h file
60  */
61 void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
62 {
63     HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
64     HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
65     SysFreeString(GameData->bstrName);
66 }
67 /*******************************************************************************
68  * GAMEUX_buildGameRegistryPath
69  *
70  * Helper function, builds registry path to key, where game's data are stored
71  *
72  * Parameters:
73  *  installScope                [I]     the scope which was used in AddGame/InstallGame call
74  *  gameInstanceId              [I]     game instance GUID
75  *  lpRegistryPath              [O]     pointer which will receive address to string
76  *                                      containing expected registry path. Path
77  *                                      is relative to HKLM registry key. It
78  *                                      must be freed by calling HeapFree(GetProcessHeap(), 0, ...)
79  *
80  * Name of game's registry key always follows patterns below:
81  *  When game is installed for current user only (installScope is GIS_CURRENT_USER):
82  *      HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
83  *          GameUX\[user's security ID]\[game instance ID]
84  *
85  *  When game is installed for all users (installScope is GIS_ALL_USERS):
86  *      HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
87  *          GameUX\Games\[game instance ID]
88  *
89  *
90  */
91 static HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
92         LPCGUID gameInstanceId,
93         LPWSTR* lpRegistryPath)
94 {
95     static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
96             'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
97             'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
98     static const WCHAR sGames[] = {'G','a','m','e','s',0};
99     static const WCHAR sBackslash[] = {'\\',0};
100
101     HRESULT hr = S_OK;
102     HANDLE hToken = NULL;
103     PTOKEN_USER pTokenUser = NULL;
104     DWORD dwLength;
105     LPWSTR lpSID = NULL;
106     WCHAR sInstanceId[40];
107     WCHAR sRegistryPath[8192];
108
109     TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
110
111     lstrcpyW(sRegistryPath, sGameUxRegistryPath);
112     lstrcatW(sRegistryPath, sBackslash);
113
114     if(installScope == GIS_CURRENT_USER)
115     {
116         /* build registry path containing user's SID */
117         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
118             hr = HRESULT_FROM_WIN32(GetLastError());
119
120         if(SUCCEEDED(hr))
121         {
122             if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
123                     GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
124                 hr = HRESULT_FROM_WIN32(GetLastError());
125
126             if(SUCCEEDED(hr))
127             {
128                 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
129                 if(!pTokenUser)
130                     hr = E_OUTOFMEMORY;
131             }
132
133             if(SUCCEEDED(hr))
134                 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
135                     hr = HRESULT_FROM_WIN32(GetLastError());
136
137             if(SUCCEEDED(hr))
138                 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
139                     hr = HRESULT_FROM_WIN32(GetLastError());
140
141             if(SUCCEEDED(hr))
142             {
143                 lstrcatW(sRegistryPath, lpSID);
144                 LocalFree(lpSID);
145             }
146
147             HeapFree(GetProcessHeap(), 0, pTokenUser);
148             CloseHandle(hToken);
149         }
150     }
151     else if(installScope == GIS_ALL_USERS)
152         /* build registry path without SID */
153         lstrcatW(sRegistryPath, sGames);
154     else
155         hr = E_INVALIDARG;
156
157     /* put game's instance id on the end of path */
158     if(SUCCEEDED(hr))
159         hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
160
161     if(SUCCEEDED(hr))
162     {
163         lstrcatW(sRegistryPath, sBackslash);
164         lstrcatW(sRegistryPath, sInstanceId);
165     }
166
167     if(SUCCEEDED(hr))
168     {
169         *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
170         if(!*lpRegistryPath)
171             hr = E_OUTOFMEMORY;
172     }
173
174     if(SUCCEEDED(hr))
175         lstrcpyW(*lpRegistryPath, sRegistryPath);
176
177     TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
178     return hr;
179 }
180 /*******************************************************************************
181  * GAMEUX_WriteRegistryRecord
182  *
183  * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
184  * structure) into expected place in registry.
185  *
186  * Parameters:
187  *  GameData                            [I]     structure with data which will
188  *                                              be written into registry.
189  *                                              Proper values of fields installScope
190  *                                              and guidInstanceId are required
191  *                                              to create registry key.
192  *
193  * Schema of naming registry keys associated with games is available in
194  * description of _buildGameRegistryPath internal function.
195  *
196  * List of registry keys associated with structure fields:
197  *  Key                              Field in GAMEUX_GAME_DATA structure
198  *   ApplicationId                    guidApplicationId
199  *   ConfigApplicationPath            sGameInstallDirectory
200  *   ConfigGDFBinaryPath              sGDFBinaryPath
201  *   Title                            bstrName
202  *
203  */
204 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
205 {
206     static const WCHAR sApplicationId[] =
207             {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
208     static const WCHAR sConfigApplicationPath[] =
209             {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
210     static const WCHAR sConfigGDFBinaryPath[] =
211             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
212     static const WCHAR sTitle[] =
213             {'T','i','t','l','e',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, 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         RegCloseKey(hKey);
255
256         if(FAILED(hr))
257         {
258             /* if something failed, remove whole key */
259             hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, 0, 0);
260             /* do not overwrite old failure code with new success code */
261             if(FAILED(hr2))
262                 hr = hr2;
263         }
264     }
265
266     HeapFree(GetProcessHeap(), 0, lpRegistryKey);
267     TRACE("returning 0x%x\n", hr);
268     return hr;
269 }
270 /*******************************************************************************
271  * GAMEUX_ProcessGameDefinitionElement
272  *
273  * Helper function, parses single element from Game Definition
274  *
275  * Parameters:
276  *  lpXMLElement                        [I]     game definition element
277  *  GameData                            [O]     structure, where parsed
278  *                                              data will be stored
279  */
280 static HRESULT GAMEUX_ProcessGameDefinitionElement(
281         IXMLDOMElement *element,
282         struct GAMEUX_GAME_DATA *GameData)
283 {
284     static const WCHAR sName[] =
285             {'N','a','m','e',0};
286
287     HRESULT hr;
288     BSTR bstrElementName;
289
290     TRACE("(%p, %p)\n", element, GameData);
291
292     hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
293     if(SUCCEEDED(hr))
294     {
295         /* check element name */
296         if(lstrcmpW(bstrElementName, sName) == 0)
297             hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
298
299         else
300             FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
301
302         SysFreeString(bstrElementName);
303     }
304
305     return hr;
306 }
307 /*******************************************************************************
308  * GAMEUX_ParseGameDefinition
309  *
310  * Helper function, loads data from given XML element into fields of GAME_DATA
311  * structure
312  *
313  * Parameters:
314  *  lpXMLGameDefinitionElement          [I]     Game Definition XML element
315  *  GameData                            [O]     structure where data loaded from
316  *                                              XML element will be stored in
317  */
318 static HRESULT GAMEUX_ParseGameDefinition(
319         IXMLDOMElement *gdElement,
320         struct GAMEUX_GAME_DATA *GameData)
321 {
322     static const WCHAR sGameId[] = {'g','a','m','e','I','D',0};
323
324     HRESULT hr = S_OK;
325     BSTR bstrAttribute;
326     VARIANT variant;
327     IXMLDOMNodeList *childrenList;
328     IXMLDOMNode *nextNode;
329     IXMLDOMElement *nextElement;
330
331     TRACE("(%p, %p)\n", gdElement, GameData);
332
333     bstrAttribute = SysAllocString(sGameId);
334     if(!bstrAttribute)
335         hr = E_OUTOFMEMORY;
336
337     hr = IXMLDOMElement_getAttribute(gdElement, bstrAttribute, &variant);
338
339     if(SUCCEEDED(hr))
340     {
341         hr = ( GUIDFromStringW(V_BSTR(&variant), &GameData->guidApplicationId)==TRUE ? S_OK : E_FAIL);
342
343         SysFreeString(V_BSTR(&variant));
344     }
345
346     SysFreeString(bstrAttribute);
347
348     /* browse subnodes */
349     if(SUCCEEDED(hr))
350         hr = IXMLDOMElement_get_childNodes(gdElement, &childrenList);
351
352     if(SUCCEEDED(hr))
353     {
354         do
355         {
356             hr = IXMLDOMNodeList_nextNode(childrenList, &nextNode);
357
358             if(hr == S_OK)
359             {
360                 hr = IXMLDOMNode_QueryInterface(nextNode, &IID_IXMLDOMElement,
361                                                 (LPVOID*)&nextElement);
362
363                 if(SUCCEEDED(hr))
364                 {
365                     hr = GAMEUX_ProcessGameDefinitionElement(nextElement, GameData);
366                     IXMLDOMElement_Release(nextElement);
367                 }
368
369                 IXMLDOMElement_Release(nextNode);
370             }
371         }
372         while(hr == S_OK);
373         hr = S_OK;
374
375         IXMLDOMNodeList_Release(childrenList);
376     }
377
378     return hr;
379 }
380 /*******************************************************************************
381  * GAMEUX_ParseGDFBinary
382  *
383  * Helper funtion, loads given binary and parses embed GDF if there's any.
384  *
385  * Parameters:
386  *  GameData                [I/O]   Structure with game's data. Content of field
387  *                                  sGDFBinaryPath defines path to binary, from
388  *                                  which embed GDF will be loaded. Data from
389  *                                  GDF will be stored in other fields of this
390  *                                  structure.
391  */
392 static HRESULT GAMEUX_ParseGDFBinary(struct GAMEUX_GAME_DATA *GameData)
393 {
394     static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
395     static const WCHAR sDATA[] = {'D','A','T','A',0};
396     static const WCHAR sSlash[] = {'/',0};
397
398     HRESULT hr = S_OK;
399     WCHAR sResourcePath[MAX_PATH];
400     VARIANT variant;
401     VARIANT_BOOL isSuccessful;
402     IXMLDOMDocument *document;
403     IXMLDOMNode *gdNode;
404     IXMLDOMElement *root, *gdElement;
405
406     TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
407
408     /* prepare path to GDF, using res:// prefix */
409     lstrcpyW(sResourcePath, sRes);
410     lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
411     lstrcatW(sResourcePath, sSlash);
412     lstrcatW(sResourcePath, sDATA);
413     lstrcatW(sResourcePath, sSlash);
414     lstrcatW(sResourcePath, ID_GDF_XML_STR);
415
416     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
417             &IID_IXMLDOMDocument, (void**)&document);
418
419     if(SUCCEEDED(hr))
420     {
421         /* load GDF into MSXML */
422         V_VT(&variant) = VT_BSTR;
423         V_BSTR(&variant) = SysAllocString(sResourcePath);
424         if(!V_BSTR(&variant))
425             hr = E_OUTOFMEMORY;
426
427         if(SUCCEEDED(hr))
428         {
429             hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
430             if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
431                 hr = E_FAIL;
432         }
433
434         SysFreeString(V_BSTR(&variant));
435
436         if(SUCCEEDED(hr))
437         {
438             hr = IXMLDOMDocument_get_documentElement(document, &root);
439             if(hr == S_FALSE)
440                 hr = E_FAIL;
441         }
442
443         if(SUCCEEDED(hr))
444         {
445             hr = IXMLDOMElement_get_firstChild(root, &gdNode);
446             if(hr == S_FALSE)
447                 hr = E_FAIL;
448
449             if(SUCCEEDED(hr))
450             {
451                 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
452                 if(SUCCEEDED(hr))
453                 {
454                     hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
455                     IXMLDOMElement_Release(gdElement);
456                 }
457
458                 IXMLDOMNode_Release(gdNode);
459             }
460
461             IXMLDOMElement_Release(root);
462         }
463
464         IXMLDOMDocument_Release(document);
465     }
466
467     return hr;
468 }
469 /*******************************************************************************
470  * GAMEUX_RegisterGame
471  *
472  * Internal helper function. Description available in gameux_private.h file
473  */
474 HRESULT WINAPI GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
475         LPCWSTR sGameInstallDirectory,
476         GAME_INSTALL_SCOPE installScope,
477         GUID *pInstanceID)
478 {
479     HRESULT hr = S_OK;
480     struct GAMEUX_GAME_DATA GameData;
481
482     TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
483
484     GAMEUX_initGameData(&GameData);
485     GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
486     lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
487     GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
488     lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
489     GameData.installScope = installScope;
490
491     /* generate GUID if it was not provided by user */
492     if(IsEqualGUID(pInstanceID, &GUID_NULL))
493         hr = CoCreateGuid(pInstanceID);
494
495     GameData.guidInstanceId = *pInstanceID;
496
497     /* load data from GDF binary */
498     if(SUCCEEDED(hr))
499         hr = GAMEUX_ParseGDFBinary(&GameData);
500
501     /* save data to registry */
502     if(SUCCEEDED(hr))
503         hr = GAMEUX_WriteRegistryRecord(&GameData);
504
505     GAMEUX_uninitGameData(&GameData);
506     TRACE("returing 0x%08x\n", hr);
507     return hr;
508 }
509 /*******************************************************************************
510  * GameExplorer implementation
511  */
512
513 typedef struct _GameExplorerImpl
514 {
515     const struct IGameExplorerVtbl *lpGameExplorerVtbl;
516     const struct IGameExplorer2Vtbl *lpGameExplorer2Vtbl;
517     LONG ref;
518 } GameExplorerImpl;
519
520 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
521 {
522     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorerVtbl));
523 }
524
525 static inline IGameExplorer* IGameExplorer_from_impl(GameExplorerImpl* This)
526 {
527     return (struct IGameExplorer*)&This->lpGameExplorerVtbl;
528 }
529
530 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
531 {
532     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorer2Vtbl));
533 }
534
535 static inline IGameExplorer2* IGameExplorer2_from_impl(GameExplorerImpl* This)
536 {
537     return (struct IGameExplorer2*)&This->lpGameExplorer2Vtbl;
538 }
539
540 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
541         IGameExplorer *iface,
542         REFIID riid,
543         void **ppvObject)
544 {
545     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
546
547     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
548
549     *ppvObject = NULL;
550
551     if(IsEqualGUID(riid, &IID_IUnknown) ||
552        IsEqualGUID(riid, &IID_IGameExplorer))
553     {
554         *ppvObject = IGameExplorer_from_impl(This);
555     }
556     else if(IsEqualGUID(riid, &IID_IGameExplorer2))
557     {
558         *ppvObject = IGameExplorer2_from_impl(This);
559     }
560     else
561     {
562         FIXME("interface %s not implemented\n", debugstr_guid(riid));
563         return E_NOINTERFACE;
564     }
565
566     IGameExplorer_AddRef(iface);
567     return S_OK;
568 }
569
570 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
571 {
572     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
573     LONG ref;
574
575     ref = InterlockedIncrement(&This->ref);
576
577     TRACE("(%p): ref=%d\n", This, ref);
578     return ref;
579 }
580
581 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
582 {
583     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
584     LONG ref;
585
586     ref = InterlockedDecrement(&This->ref);
587     TRACE("(%p): ref=%d\n", This, ref);
588
589     if(ref == 0)
590     {
591         TRACE("freeing GameExplorer object\n");
592         HeapFree(GetProcessHeap(), 0, This);
593     }
594
595     return ref;
596 }
597
598 static HRESULT WINAPI GameExplorerImpl_AddGame(
599         IGameExplorer *iface,
600         BSTR bstrGDFBinaryPath,
601         BSTR sGameInstallDirectory,
602         GAME_INSTALL_SCOPE installScope,
603         GUID *pInstanceID)
604 {
605     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
606     TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
607     return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
608 }
609
610 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
611         IGameExplorer *iface,
612         GUID instanceID)
613 {
614     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
615
616     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
617     FIXME("stub\n");
618     return E_NOTIMPL;
619 }
620
621 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
622         IGameExplorer *iface,
623         GUID instanceID)
624 {
625     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
626
627     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
628     FIXME("stub\n");
629     return E_NOTIMPL;
630 }
631
632 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
633         IGameExplorer *iface,
634         BSTR sGDFBinaryPath,
635         BOOL *pHasAccess)
636 {
637     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
638
639     TRACE("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
640     FIXME("stub\n");
641     return E_NOTIMPL;
642 }
643
644 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
645 {
646     GameExplorerImpl_QueryInterface,
647     GameExplorerImpl_AddRef,
648     GameExplorerImpl_Release,
649     GameExplorerImpl_AddGame,
650     GameExplorerImpl_RemoveGame,
651     GameExplorerImpl_UpdateGame,
652     GameExplorerImpl_VerifyAccess
653 };
654
655
656 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
657         IGameExplorer2 *iface,
658         REFIID riid,
659         void **ppvObject)
660 {
661     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
662     return GameExplorerImpl_QueryInterface(IGameExplorer_from_impl(This), riid, ppvObject);
663 }
664
665 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
666 {
667     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
668     return GameExplorerImpl_AddRef(IGameExplorer_from_impl(This));
669 }
670
671 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
672 {
673     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
674     return GameExplorerImpl_Release(IGameExplorer_from_impl(This));
675 }
676
677 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
678         IGameExplorer2 *iface,
679         LPCWSTR binaryGDFPath,
680         BOOL *pHasAccess)
681 {
682     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
683     FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
684     return E_NOTIMPL;
685 }
686
687 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
688         IGameExplorer2 *iface,
689         LPCWSTR binaryGDFPath,
690         LPCWSTR installDirectory,
691         GAME_INSTALL_SCOPE installScope)
692 {
693     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
694     FIXME("stub (%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
695     return E_NOTIMPL;
696 }
697
698 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
699         IGameExplorer2 *iface,
700         LPCWSTR binaryGDFPath)
701 {
702     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
703     FIXME("stub (%p, %s)\n", This, debugstr_w(binaryGDFPath));
704     return E_NOTIMPL;
705 }
706
707 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
708 {
709     GameExplorer2Impl_QueryInterface,
710     GameExplorer2Impl_AddRef,
711     GameExplorer2Impl_Release,
712     GameExplorer2Impl_InstallGame,
713     GameExplorer2Impl_UninstallGame,
714     GameExplorer2Impl_CheckAccess
715 };
716
717 /*
718  * Construction routine
719  */
720 HRESULT GameExplorer_create(
721         IUnknown* pUnkOuter,
722         IUnknown** ppObj)
723 {
724     GameExplorerImpl *pGameExplorer;
725
726     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
727
728     pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
729
730     if(!pGameExplorer)
731         return E_OUTOFMEMORY;
732
733     pGameExplorer->lpGameExplorerVtbl = &GameExplorerImplVtbl;
734     pGameExplorer->lpGameExplorer2Vtbl = &GameExplorer2ImplVtbl;
735     pGameExplorer->ref = 1;
736
737     *ppObj = (IUnknown*)(&pGameExplorer->lpGameExplorerVtbl);
738
739     TRACE("returning iface: %p\n", *ppObj);
740     return S_OK;
741 }