wined3d: Pass an IWineD3DBaseTextureImpl pointer to basetexture_get_lod().
[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. Description available in gameux_private.h file
48  */
49 void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
50 {
51     GameData->sGDFBinaryPath = NULL;
52     GameData->sGameInstallDirectory = NULL;
53     GameData->bstrName = NULL;
54     GameData->bstrDescription = 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     SysFreeString(GameData->bstrDescription);
67 }
68 /*******************************************************************************
69  * GAMEUX_buildGameRegistryPath
70  *
71  * Internal helper function. Description available in gameux_private.h file
72  */
73 HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
74         LPCGUID gameInstanceId,
75         LPWSTR* lpRegistryPath)
76 {
77     static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
78             'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
79             'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
80     static const WCHAR sGames[] = {'G','a','m','e','s',0};
81     static const WCHAR sBackslash[] = {'\\',0};
82
83     HRESULT hr = S_OK;
84     HANDLE hToken = NULL;
85     PTOKEN_USER pTokenUser = NULL;
86     DWORD dwLength;
87     LPWSTR lpSID = NULL;
88     WCHAR sInstanceId[40];
89     WCHAR sRegistryPath[8192];
90
91     TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
92
93     /* this will make freeing it easier for user */
94     *lpRegistryPath = NULL;
95
96     lstrcpyW(sRegistryPath, sGameUxRegistryPath);
97     lstrcatW(sRegistryPath, sBackslash);
98
99     if(installScope == GIS_CURRENT_USER)
100     {
101         /* build registry path containing user's SID */
102         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
103             hr = HRESULT_FROM_WIN32(GetLastError());
104
105         if(SUCCEEDED(hr))
106         {
107             if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
108                     GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
109                 hr = HRESULT_FROM_WIN32(GetLastError());
110
111             if(SUCCEEDED(hr))
112             {
113                 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
114                 if(!pTokenUser)
115                     hr = E_OUTOFMEMORY;
116             }
117
118             if(SUCCEEDED(hr))
119                 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
120                     hr = HRESULT_FROM_WIN32(GetLastError());
121
122             if(SUCCEEDED(hr))
123                 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
124                     hr = HRESULT_FROM_WIN32(GetLastError());
125
126             if(SUCCEEDED(hr))
127             {
128                 lstrcatW(sRegistryPath, lpSID);
129                 LocalFree(lpSID);
130             }
131
132             HeapFree(GetProcessHeap(), 0, pTokenUser);
133             CloseHandle(hToken);
134         }
135     }
136     else if(installScope == GIS_ALL_USERS)
137         /* build registry path without SID */
138         lstrcatW(sRegistryPath, sGames);
139     else
140         hr = E_INVALIDARG;
141
142     /* put game's instance id on the end of path, only if instance id was given */
143     if(gameInstanceId)
144     {
145         if(SUCCEEDED(hr))
146             hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
147
148         if(SUCCEEDED(hr))
149         {
150             lstrcatW(sRegistryPath, sBackslash);
151             lstrcatW(sRegistryPath, sInstanceId);
152         }
153     }
154
155     if(SUCCEEDED(hr))
156     {
157         *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
158         if(!*lpRegistryPath)
159             hr = E_OUTOFMEMORY;
160     }
161
162     if(SUCCEEDED(hr))
163         lstrcpyW(*lpRegistryPath, sRegistryPath);
164
165     TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
166     return hr;
167 }
168 /*******************************************************************************
169  * GAMEUX_WriteRegistryRecord
170  *
171  * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
172  * structure) into expected place in registry.
173  *
174  * Parameters:
175  *  GameData                            [I]     structure with data which will
176  *                                              be written into registry.
177  *                                              Proper values of fields installScope
178  *                                              and guidInstanceId are required
179  *                                              to create registry key.
180  *
181  * Schema of naming registry keys associated with games is available in
182  * description of _buildGameRegistryPath internal function.
183  *
184  * List of registry keys associated with structure fields:
185  *  Key                              Field in GAMEUX_GAME_DATA structure
186  *   ApplicationId                    guidApplicationId
187  *   ConfigApplicationPath            sGameInstallDirectory
188  *   ConfigGDFBinaryPath              sGDFBinaryPath
189  *   Title                            bstrName
190  *
191  */
192 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
193 {
194     static const WCHAR sApplicationId[] =
195             {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
196     static const WCHAR sConfigApplicationPath[] =
197             {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
198     static const WCHAR sConfigGDFBinaryPath[] =
199             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
200     static const WCHAR sTitle[] =
201             {'T','i','t','l','e',0};
202     static const WCHAR sDescription[] =
203             {'D','e','s','c','r','i','p','t','i','o','n',0};
204
205     HRESULT hr, hr2;
206     LPWSTR lpRegistryKey;
207     HKEY hKey;
208     WCHAR sGameApplicationId[40];
209
210     TRACE("(%p)\n", GameData);
211
212     hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
213
214     if(SUCCEEDED(hr))
215         hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
216
217     if(SUCCEEDED(hr))
218         hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
219                                                 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL,
220                                                 &hKey, NULL));
221
222     if(SUCCEEDED(hr))
223     {
224         /* write game data to registry key */
225         hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
226                                                REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
227                                                (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
228
229         if(SUCCEEDED(hr))
230             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
231                                                    REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
232                                                    (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
233
234         if(SUCCEEDED(hr))
235             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
236                                                    REG_SZ, (LPBYTE)(sGameApplicationId),
237                                                    (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
238
239         if(SUCCEEDED(hr))
240             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
241                                                    REG_SZ, (LPBYTE)(GameData->bstrName),
242                                                    (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
243
244         if(SUCCEEDED(hr))
245             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0,
246                                                    REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName),
247                                                    (lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR)));
248
249         RegCloseKey(hKey);
250
251         if(FAILED(hr))
252         {
253             /* if something failed, remove whole key */
254             hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, KEY_WOW64_64KEY, 0);
255             /* do not overwrite old failure code with new success code */
256             if(FAILED(hr2))
257                 hr = hr2;
258         }
259     }
260
261     HeapFree(GetProcessHeap(), 0, lpRegistryKey);
262     TRACE("returning 0x%x\n", hr);
263     return hr;
264 }
265 /*******************************************************************************
266  * GAMEUX_ProcessGameDefinitionElement
267  *
268  * Helper function, parses single element from Game Definition
269  *
270  * Parameters:
271  *  lpXMLElement                        [I]     game definition element
272  *  GameData                            [O]     structure, where parsed
273  *                                              data will be stored
274  */
275 static HRESULT GAMEUX_ProcessGameDefinitionElement(
276         IXMLDOMElement *element,
277         struct GAMEUX_GAME_DATA *GameData)
278 {
279     static const WCHAR sName[] =
280             {'N','a','m','e',0};
281     static const WCHAR sDescription[] =
282             {'D','e','s','c','r','i','p','t','i','o','n',0};
283
284     HRESULT hr;
285     BSTR bstrElementName;
286
287     TRACE("(%p, %p)\n", element, GameData);
288
289     hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
290     if(SUCCEEDED(hr))
291     {
292         /* check element name */
293         if(lstrcmpW(bstrElementName, sName) == 0)
294             hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
295
296         else if(lstrcmpW(bstrElementName, sDescription) == 0)
297             hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
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 function, 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_RemoveRegistryRecord
471  *
472  * Helper function, removes registry key associated with given game instance
473  */
474 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
475 {
476     HRESULT hr;
477     LPWSTR lpRegistryPath = NULL;
478     TRACE("(%s)\n", debugstr_guid(pInstanceID));
479
480     /* first, check is game installed for all users */
481     hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath);
482     if(SUCCEEDED(hr))
483         hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
484
485     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
486
487     /* if not, check current user */
488     if(FAILED(hr))
489     {
490         hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath);
491         if(SUCCEEDED(hr))
492             hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0));
493
494         HeapFree(GetProcessHeap(), 0, lpRegistryPath);
495     }
496
497     return hr;
498 }
499 /*******************************************************************************
500  * GAMEUX_RegisterGame
501  *
502  * Internal helper function. Description available in gameux_private.h file
503  */
504 HRESULT WINAPI GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
505         LPCWSTR sGameInstallDirectory,
506         GAME_INSTALL_SCOPE installScope,
507         GUID *pInstanceID)
508 {
509     HRESULT hr = S_OK;
510     struct GAMEUX_GAME_DATA GameData;
511
512     TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
513
514     GAMEUX_initGameData(&GameData);
515     GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
516     lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
517     GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
518     lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
519     GameData.installScope = installScope;
520
521     /* generate GUID if it was not provided by user */
522     if(IsEqualGUID(pInstanceID, &GUID_NULL))
523         hr = CoCreateGuid(pInstanceID);
524
525     GameData.guidInstanceId = *pInstanceID;
526
527     /* load data from GDF binary */
528     if(SUCCEEDED(hr))
529         hr = GAMEUX_ParseGDFBinary(&GameData);
530
531     /* save data to registry */
532     if(SUCCEEDED(hr))
533         hr = GAMEUX_WriteRegistryRecord(&GameData);
534
535     GAMEUX_uninitGameData(&GameData);
536     TRACE("returning 0x%08x\n", hr);
537     return hr;
538 }
539 /*******************************************************************************
540  * GAMEUX_IsGameKeyExist
541  *
542  * Helper function, checks if game's registry ath exists in given scope
543  *
544  * Parameters:
545  *  installScope            [I]     scope to search game in
546  *  InstanceID              [I]     game instance identifier
547  *  lpRegistryPath          [O]     place to store address of registry path to
548  *                                  the game. It is filled only if key exists.
549  *                                  It must be freed by HeapFree(GetProcessHeap(), 0, ...)
550  *
551  * Returns:
552  *  S_OK                key was found properly
553  *  S_FALSE             key does not exists
554  *
555  */
556 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
557     LPCGUID InstanceID,
558     LPWSTR* lpRegistryPath) {
559
560     HRESULT hr;
561     HKEY hKey;
562
563     hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
564
565     if(SUCCEEDED(hr))
566         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
567                                               0, KEY_WOW64_64KEY, &hKey));
568
569     if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
570         hr = S_FALSE;
571
572     if(hr == S_OK)
573         RegCloseKey(hKey);
574     else
575     {
576         /* if the key does not exist or another error occurred, do not return the path */
577         HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
578         *lpRegistryPath = NULL;
579     }
580
581     return hr;
582 }
583 /*******************************************************************************
584  * GAMEUX_LoadRegistryString
585  *
586  * Helper function, loads string from registry value and allocates buffer for it
587  */
588 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
589         LPCWSTR lpRegistryKey,
590         LPCWSTR lpRegistryValue,
591         LPWSTR* lpValue)
592 {
593     HRESULT hr;
594     DWORD dwSize;
595
596     *lpValue = NULL;
597
598     hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
599             RRF_RT_REG_SZ, NULL, NULL, &dwSize));
600
601     if(SUCCEEDED(hr))
602     {
603         *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
604         if(!*lpValue)
605             hr = E_OUTOFMEMORY;
606     }
607
608     if(SUCCEEDED(hr))
609         hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
610                 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
611
612     return hr;
613 }
614 /*******************************************************************************
615  * GAMEUX_UpdateGame
616  *
617  * Helper function, updates stored data about game with given InstanceID
618  */
619 static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) {
620     static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
621     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};
622
623     HRESULT hr;
624     GAME_INSTALL_SCOPE installScope;
625     LPWSTR lpRegistryPath;
626     LPWSTR lpGDFBinaryPath, lpGameInstallDirectory;
627
628     TRACE("(%p)\n", debugstr_guid(InstanceID));
629
630     /* first, check is game exists in CURRENT_USER scope  */
631     installScope = GIS_CURRENT_USER;
632     hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
633
634     if(hr == S_FALSE)
635     {
636         /* game not found in CURRENT_USER scope, let's check in ALL_USERS */
637         installScope = GIS_ALL_USERS;
638         hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
639     }
640
641     if(hr == S_FALSE)
642         /* still not found? let's inform user that game does not exists */
643         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
644
645     if(SUCCEEDED(hr))
646     {
647         /* game found, it's registry path is in lpRegistryPath and install
648          * scope in installScope */
649         TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath));
650
651         /* first, read required data about game */
652         hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
653             sConfigGDFBinaryPath, &lpGDFBinaryPath);
654
655         if(SUCCEEDED(hr))
656             hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
657                 sConfigApplicationPath, &lpGameInstallDirectory);
658
659         /* now remove currently existing registry key */
660         if(SUCCEEDED(hr))
661             hr = GAMEUX_RemoveRegistryRecord(InstanceID);
662
663         /* and add it again, it will cause in reparsing of whole GDF */
664         if(SUCCEEDED(hr))
665             hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory,
666                                      installScope, InstanceID);
667
668         HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
669         HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
670     }
671
672     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
673     TRACE("returning 0x%x\n", hr);
674     return hr;
675 }
676 /*******************************************************************************
677  * GAMEUX_FindGameInstanceId
678  *
679  * Internal helper function. Description available in gameux_private.h file
680  */
681 HRESULT GAMEUX_FindGameInstanceId(
682         LPCWSTR sGDFBinaryPath,
683         GAME_INSTALL_SCOPE installScope,
684         GUID* pInstanceId)
685 {
686     static const WCHAR sConfigGDFBinaryPath[] =
687             {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
688
689     HRESULT hr;
690     BOOL found = FALSE;
691     LPWSTR lpRegistryPath = NULL;
692     HKEY hRootKey;
693     DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i;
694     LPWSTR lpName = NULL, lpValue = NULL;
695
696     hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
697
698     if(SUCCEEDED(hr))
699         /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */
700         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
701                 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey));
702
703     if(SUCCEEDED(hr))
704     {
705         hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
706                 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
707
708         if(SUCCEEDED(hr))
709         {
710             ++dwMaxSubKeyLen; /* for string terminator */
711             lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
712             if(!lpName) hr = E_OUTOFMEMORY;
713         }
714
715         if(SUCCEEDED(hr))
716         {
717             for(i=0; i<dwSubKeys && !found; ++i)
718             {
719                 dwSubKeyLen = dwMaxSubKeyLen;
720                 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
721                         NULL, NULL, NULL, NULL));
722
723                 if(SUCCEEDED(hr))
724                     hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
725                                              sConfigGDFBinaryPath, &lpValue);
726
727                 if(SUCCEEDED(hr))
728                     if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
729                     {
730                         /* key found, let's copy instance id and exit */
731                         hr = (GUIDFromStringW(lpName, pInstanceId) ? S_OK : E_FAIL);
732                         found = TRUE;
733                     }
734                 HeapFree(GetProcessHeap(), 0, lpValue);
735             }
736         }
737
738         HeapFree(GetProcessHeap(), 0, lpName);
739         RegCloseKey(hRootKey);
740     }
741
742     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
743
744     if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
745         hr = S_FALSE;
746
747     return hr;
748 }
749 /*******************************************************************************
750  * GameExplorer implementation
751  */
752
753 typedef struct _GameExplorerImpl
754 {
755     const struct IGameExplorerVtbl *lpGameExplorerVtbl;
756     const struct IGameExplorer2Vtbl *lpGameExplorer2Vtbl;
757     LONG ref;
758 } GameExplorerImpl;
759
760 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
761 {
762     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorerVtbl));
763 }
764
765 static inline IGameExplorer* IGameExplorer_from_impl(GameExplorerImpl* This)
766 {
767     return (struct IGameExplorer*)&This->lpGameExplorerVtbl;
768 }
769
770 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
771 {
772     return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorer2Vtbl));
773 }
774
775 static inline IGameExplorer2* IGameExplorer2_from_impl(GameExplorerImpl* This)
776 {
777     return (struct IGameExplorer2*)&This->lpGameExplorer2Vtbl;
778 }
779
780 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
781         IGameExplorer *iface,
782         REFIID riid,
783         void **ppvObject)
784 {
785     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
786
787     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
788
789     *ppvObject = NULL;
790
791     if(IsEqualGUID(riid, &IID_IUnknown) ||
792        IsEqualGUID(riid, &IID_IGameExplorer))
793     {
794         *ppvObject = IGameExplorer_from_impl(This);
795     }
796     else if(IsEqualGUID(riid, &IID_IGameExplorer2))
797     {
798         *ppvObject = IGameExplorer2_from_impl(This);
799     }
800     else
801     {
802         FIXME("interface %s not implemented\n", debugstr_guid(riid));
803         return E_NOINTERFACE;
804     }
805
806     IGameExplorer_AddRef(iface);
807     return S_OK;
808 }
809
810 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
811 {
812     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
813     LONG ref;
814
815     ref = InterlockedIncrement(&This->ref);
816
817     TRACE("(%p): ref=%d\n", This, ref);
818     return ref;
819 }
820
821 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
822 {
823     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
824     LONG ref;
825
826     ref = InterlockedDecrement(&This->ref);
827     TRACE("(%p): ref=%d\n", This, ref);
828
829     if(ref == 0)
830     {
831         TRACE("freeing GameExplorer object\n");
832         HeapFree(GetProcessHeap(), 0, This);
833     }
834
835     return ref;
836 }
837
838 static HRESULT WINAPI GameExplorerImpl_AddGame(
839         IGameExplorer *iface,
840         BSTR bstrGDFBinaryPath,
841         BSTR sGameInstallDirectory,
842         GAME_INSTALL_SCOPE installScope,
843         GUID *pInstanceID)
844 {
845     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
846     TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
847     return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
848 }
849
850 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
851         IGameExplorer *iface,
852         GUID instanceID)
853 {
854     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
855
856     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
857     return GAMEUX_RemoveRegistryRecord(&instanceID);
858 }
859
860 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
861         IGameExplorer *iface,
862         GUID instanceID)
863 {
864     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
865
866     TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
867     return GAMEUX_UpdateGame(&instanceID);
868 }
869
870 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
871         IGameExplorer *iface,
872         BSTR sGDFBinaryPath,
873         BOOL *pHasAccess)
874 {
875     GameExplorerImpl *This = impl_from_IGameExplorer(iface);
876
877     FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
878     *pHasAccess = TRUE;
879     return S_OK;
880 }
881
882 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
883 {
884     GameExplorerImpl_QueryInterface,
885     GameExplorerImpl_AddRef,
886     GameExplorerImpl_Release,
887     GameExplorerImpl_AddGame,
888     GameExplorerImpl_RemoveGame,
889     GameExplorerImpl_UpdateGame,
890     GameExplorerImpl_VerifyAccess
891 };
892
893
894 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
895         IGameExplorer2 *iface,
896         REFIID riid,
897         void **ppvObject)
898 {
899     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
900     return GameExplorerImpl_QueryInterface(IGameExplorer_from_impl(This), riid, ppvObject);
901 }
902
903 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
904 {
905     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
906     return GameExplorerImpl_AddRef(IGameExplorer_from_impl(This));
907 }
908
909 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
910 {
911     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
912     return GameExplorerImpl_Release(IGameExplorer_from_impl(This));
913 }
914
915 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
916         IGameExplorer2 *iface,
917         LPCWSTR binaryGDFPath,
918         BOOL *pHasAccess)
919 {
920     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
921     FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
922     return E_NOTIMPL;
923 }
924
925 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
926         IGameExplorer2 *iface,
927         LPCWSTR binaryGDFPath,
928         LPCWSTR installDirectory,
929         GAME_INSTALL_SCOPE installScope)
930 {
931     HRESULT hr;
932     GUID instanceId;
933     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
934
935     TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
936
937     if(!binaryGDFPath)
938         return E_INVALIDARG;
939
940     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
941
942     if(hr == S_FALSE)
943         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
944
945     if(hr == S_FALSE)
946     {
947         /* if game isn't yet registered, then install it */
948         instanceId = GUID_NULL;
949         hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
950     }
951     else if(hr == S_OK)
952         /* otherwise, update game */
953         hr = GAMEUX_UpdateGame(&instanceId);
954
955     return hr;
956 }
957
958 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
959         IGameExplorer2 *iface,
960         LPCWSTR binaryGDFPath)
961 {
962     HRESULT hr;
963     GUID instanceId;
964     GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
965     TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
966
967     if(!binaryGDFPath)
968         return E_INVALIDARG;
969
970     hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
971
972     if(hr == S_FALSE)
973         hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
974
975     if(hr == S_OK)
976         hr = GAMEUX_RemoveRegistryRecord(&instanceId);
977
978     return hr;
979 }
980
981 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
982 {
983     GameExplorer2Impl_QueryInterface,
984     GameExplorer2Impl_AddRef,
985     GameExplorer2Impl_Release,
986     GameExplorer2Impl_InstallGame,
987     GameExplorer2Impl_UninstallGame,
988     GameExplorer2Impl_CheckAccess
989 };
990
991 /*
992  * Construction routine
993  */
994 HRESULT GameExplorer_create(
995         IUnknown* pUnkOuter,
996         IUnknown** ppObj)
997 {
998     GameExplorerImpl *pGameExplorer;
999
1000     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1001
1002     pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1003
1004     if(!pGameExplorer)
1005         return E_OUTOFMEMORY;
1006
1007     pGameExplorer->lpGameExplorerVtbl = &GameExplorerImplVtbl;
1008     pGameExplorer->lpGameExplorer2Vtbl = &GameExplorer2ImplVtbl;
1009     pGameExplorer->ref = 1;
1010
1011     *ppObj = (IUnknown*)(&pGameExplorer->lpGameExplorerVtbl);
1012
1013     TRACE("returning iface: %p\n", *ppObj);
1014     return S_OK;
1015 }