Add AppDefaults app selection to control panel
[wine/gsoc-2012-control.git] / dlls / gameux / gameexplorer.c
blob556bf3695f018481e8a080bf6edfb9cbba01451d
1 /*
2 * Gameux library coclass GameExplorer implementation
4 * Copyright (C) 2010 Mariusz PluciƄski
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.
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.
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
20 #define COBJMACROS
22 #include "config.h"
24 #include "ole2.h"
25 #include "sddl.h"
27 #include "gameux.h"
28 #include "gameux_private.h"
30 #include "initguid.h"
31 #include "msxml2.h"
33 #include "wine/debug.h"
34 #include "winreg.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
38 /* function from Shell32, not defined in header */
39 extern BOOL WINAPI GUIDFromStringW(LPCWSTR psz, LPGUID pguid);
41 /*******************************************************************************
42 * GameUX helper functions
44 /*******************************************************************************
45 * GAMEUX_initGameData
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
51 * Parameters:
52 * GameData [I/O] pointer to structure to initialize
54 static void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
56 GameData->sGDFBinaryPath = NULL;
57 GameData->sGameInstallDirectory = NULL;
58 GameData->bstrName = NULL;
59 GameData->bstrDescription = NULL;
61 /*******************************************************************************
62 * GAMEUX_uninitGameData
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
68 * Parameters:
69 * GameData [I/O] pointer to structure to uninitialize
71 static void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
73 HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
74 HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
75 SysFreeString(GameData->bstrName);
76 SysFreeString(GameData->bstrDescription);
78 /*******************************************************************************
79 * GAMEUX_buildGameRegistryPath
81 * Internal helper function. Description available in gameux_private.h file
83 HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
84 LPCGUID gameInstanceId,
85 LPWSTR* lpRegistryPath)
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};
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];
101 TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
103 /* this will make freeing it easier for user */
104 *lpRegistryPath = NULL;
106 lstrcpyW(sRegistryPath, sGameUxRegistryPath);
107 lstrcatW(sRegistryPath, sBackslash);
109 if(installScope == GIS_CURRENT_USER)
111 /* build registry path containing user's SID */
112 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
113 hr = HRESULT_FROM_WIN32(GetLastError());
115 if(SUCCEEDED(hr))
117 if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
118 GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
119 hr = HRESULT_FROM_WIN32(GetLastError());
121 if(SUCCEEDED(hr))
123 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
124 if(!pTokenUser)
125 hr = E_OUTOFMEMORY;
128 if(SUCCEEDED(hr))
129 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
130 hr = HRESULT_FROM_WIN32(GetLastError());
132 if(SUCCEEDED(hr))
133 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
134 hr = HRESULT_FROM_WIN32(GetLastError());
136 if(SUCCEEDED(hr))
138 lstrcatW(sRegistryPath, lpSID);
139 LocalFree(lpSID);
142 HeapFree(GetProcessHeap(), 0, pTokenUser);
143 CloseHandle(hToken);
146 else if(installScope == GIS_ALL_USERS)
147 /* build registry path without SID */
148 lstrcatW(sRegistryPath, sGames);
149 else
150 hr = E_INVALIDARG;
152 /* put game's instance id on the end of path, only if instance id was given */
153 if(gameInstanceId)
155 if(SUCCEEDED(hr))
156 hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
158 if(SUCCEEDED(hr))
160 lstrcatW(sRegistryPath, sBackslash);
161 lstrcatW(sRegistryPath, sInstanceId);
165 if(SUCCEEDED(hr))
167 *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
168 if(!*lpRegistryPath)
169 hr = E_OUTOFMEMORY;
172 if(SUCCEEDED(hr))
173 lstrcpyW(*lpRegistryPath, sRegistryPath);
175 TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
176 return hr;
178 /*******************************************************************************
179 * GAMEUX_WriteRegistryRecord
181 * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
182 * structure) into expected place in registry.
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.
191 * Schema of naming registry keys associated with games is available in
192 * description of _buildGameRegistryPath internal function.
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
202 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
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};
215 HRESULT hr, hr2;
216 LPWSTR lpRegistryKey;
217 HKEY hKey;
218 WCHAR sGameApplicationId[40];
220 TRACE("(%p)\n", GameData);
222 hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
224 if(SUCCEEDED(hr))
225 hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
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));
232 if(SUCCEEDED(hr))
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)));
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)));
244 if(SUCCEEDED(hr))
245 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
246 REG_SZ, (LPBYTE)(sGameApplicationId),
247 (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
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)));
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)));
259 RegCloseKey(hKey);
261 if(FAILED(hr))
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;
271 HeapFree(GetProcessHeap(), 0, lpRegistryKey);
272 TRACE("returning 0x%x\n", hr);
273 return hr;
275 /*******************************************************************************
276 * GAMEUX_ProcessGameDefinitionElement
278 * Helper function, parses single element from Game Definition
280 * Parameters:
281 * lpXMLElement [I] game definition element
282 * GameData [O] structure, where parsed
283 * data will be stored
285 static HRESULT GAMEUX_ProcessGameDefinitionElement(
286 IXMLDOMElement *element,
287 struct GAMEUX_GAME_DATA *GameData)
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};
294 HRESULT hr;
295 BSTR bstrElementName;
297 TRACE("(%p, %p)\n", element, GameData);
299 hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
300 if(SUCCEEDED(hr))
302 /* check element name */
303 if(lstrcmpW(bstrElementName, sName) == 0)
304 hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
306 else if(lstrcmpW(bstrElementName, sDescription) == 0)
307 hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription);
309 else
310 FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
312 SysFreeString(bstrElementName);
315 return hr;
317 /*******************************************************************************
318 * GAMEUX_ParseGameDefinition
320 * Helper function, loads data from given XML element into fields of GAME_DATA
321 * structure
323 * Parameters:
324 * lpXMLGameDefinitionElement [I] Game Definition XML element
325 * GameData [O] structure where data loaded from
326 * XML element will be stored in
328 static HRESULT GAMEUX_ParseGameDefinition(
329 IXMLDOMElement *gdElement,
330 struct GAMEUX_GAME_DATA *GameData)
332 static const WCHAR sGameId[] = {'g','a','m','e','I','D',0};
334 HRESULT hr = S_OK;
335 BSTR bstrAttribute;
336 VARIANT variant;
337 IXMLDOMNodeList *childrenList;
338 IXMLDOMNode *nextNode;
339 IXMLDOMElement *nextElement;
341 TRACE("(%p, %p)\n", gdElement, GameData);
343 bstrAttribute = SysAllocString(sGameId);
344 if(!bstrAttribute)
345 hr = E_OUTOFMEMORY;
347 hr = IXMLDOMElement_getAttribute(gdElement, bstrAttribute, &variant);
349 if(SUCCEEDED(hr))
351 hr = ( GUIDFromStringW(V_BSTR(&variant), &GameData->guidApplicationId)==TRUE ? S_OK : E_FAIL);
353 SysFreeString(V_BSTR(&variant));
356 SysFreeString(bstrAttribute);
358 /* browse subnodes */
359 if(SUCCEEDED(hr))
360 hr = IXMLDOMElement_get_childNodes(gdElement, &childrenList);
362 if(SUCCEEDED(hr))
366 hr = IXMLDOMNodeList_nextNode(childrenList, &nextNode);
368 if(hr == S_OK)
370 hr = IXMLDOMNode_QueryInterface(nextNode, &IID_IXMLDOMElement,
371 (LPVOID*)&nextElement);
373 if(SUCCEEDED(hr))
375 hr = GAMEUX_ProcessGameDefinitionElement(nextElement, GameData);
376 IXMLDOMElement_Release(nextElement);
379 IXMLDOMElement_Release(nextNode);
382 while(hr == S_OK);
383 hr = S_OK;
385 IXMLDOMNodeList_Release(childrenList);
388 return hr;
391 struct parse_gdf_thread_param
393 struct GAMEUX_GAME_DATA *GameData;
394 HRESULT hr;
397 /*******************************************************************************
398 * GAMEUX_ParseGDFBinary
400 * Helper function, loads given binary and parses embed GDF if there's any.
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.
409 static DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param)
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};
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;
425 TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
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);
435 CoInitialize(NULL);
437 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
438 &IID_IXMLDOMDocument, (void**)&document);
440 if(SUCCEEDED(hr))
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;
448 if(SUCCEEDED(hr))
450 hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
451 if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
452 hr = E_FAIL;
455 SysFreeString(V_BSTR(&variant));
457 if(SUCCEEDED(hr))
459 hr = IXMLDOMDocument_get_documentElement(document, &root);
460 if(hr == S_FALSE)
461 hr = E_FAIL;
464 if(SUCCEEDED(hr))
466 hr = IXMLDOMElement_get_firstChild(root, &gdNode);
467 if(hr == S_FALSE)
468 hr = E_FAIL;
470 if(SUCCEEDED(hr))
472 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
473 if(SUCCEEDED(hr))
475 hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
476 IXMLDOMElement_Release(gdElement);
479 IXMLDOMNode_Release(gdNode);
482 IXMLDOMElement_Release(root);
485 IXMLDOMDocument_Release(document);
488 CoUninitialize();
489 ctx->hr = hr;
490 return 0;
493 /*******************************************************************
494 * GAMEUX_RemoveRegistryRecord
496 * Helper function, removes registry key associated with given game instance
498 static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID)
500 HRESULT hr;
501 LPWSTR lpRegistryPath = NULL;
502 TRACE("(%s)\n", debugstr_guid(pInstanceID));
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));
509 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
511 /* if not, check current user */
512 if(FAILED(hr))
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));
518 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
521 return hr;
523 /*******************************************************************************
524 * GAMEUX_RegisterGame
526 * Internal helper function. Registers game associated with given GDF binary in
527 * Game Explorer. Implemented in gameexplorer.c
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
540 static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
541 LPCWSTR sGameInstallDirectory,
542 GAME_INSTALL_SCOPE installScope,
543 GUID *pInstanceID)
545 HRESULT hr = S_OK;
546 struct GAMEUX_GAME_DATA GameData;
548 TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
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;
557 /* generate GUID if it was not provided by user */
558 if(IsEqualGUID(pInstanceID, &GUID_NULL))
559 hr = CoCreateGuid(pInstanceID);
561 GameData.guidInstanceId = *pInstanceID;
563 /* load data from GDF binary */
564 if(SUCCEEDED(hr))
566 struct parse_gdf_thread_param thread_param;
567 HANDLE thread;
568 DWORD ret;
570 thread_param.GameData = &GameData;
571 if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret)))
573 ERR("Failed to create thread.\n");
574 hr = E_FAIL;
575 goto done;
577 ret = WaitForSingleObject(thread, INFINITE);
578 CloseHandle(thread);
579 if(ret != WAIT_OBJECT_0)
581 ERR("Wait failed (%#x).\n", ret);
582 hr = E_FAIL;
583 goto done;
585 hr = thread_param.hr;
588 /* save data to registry */
589 if(SUCCEEDED(hr))
590 hr = GAMEUX_WriteRegistryRecord(&GameData);
592 done:
593 GAMEUX_uninitGameData(&GameData);
594 TRACE("returning 0x%08x\n", hr);
595 return hr;
597 /*******************************************************************************
598 * GAMEUX_IsGameKeyExist
600 * Helper function, checks if game's registry ath exists in given scope
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, ...)
609 * Returns:
610 * S_OK key was found properly
611 * S_FALSE key does not exists
614 static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope,
615 LPCGUID InstanceID,
616 LPWSTR* lpRegistryPath) {
618 HRESULT hr;
619 HKEY hKey;
621 hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath);
623 if(SUCCEEDED(hr))
624 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath,
625 0, KEY_WOW64_64KEY, &hKey));
627 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
628 hr = S_FALSE;
630 if(hr == S_OK)
631 RegCloseKey(hKey);
632 else
634 /* if the key does not exist or another error occurred, do not return the path */
635 HeapFree(GetProcessHeap(), 0, *lpRegistryPath);
636 *lpRegistryPath = NULL;
639 return hr;
641 /*******************************************************************************
642 * GAMEUX_LoadRegistryString
644 * Helper function, loads string from registry value and allocates buffer for it
646 static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey,
647 LPCWSTR lpRegistryKey,
648 LPCWSTR lpRegistryValue,
649 LPWSTR* lpValue)
651 HRESULT hr;
652 DWORD dwSize;
654 *lpValue = NULL;
656 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
657 RRF_RT_REG_SZ, NULL, NULL, &dwSize));
659 if(SUCCEEDED(hr))
661 *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
662 if(!*lpValue)
663 hr = E_OUTOFMEMORY;
666 if(SUCCEEDED(hr))
667 hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue,
668 RRF_RT_REG_SZ, NULL, *lpValue, &dwSize));
670 return hr;
672 /*******************************************************************************
673 * GAMEUX_UpdateGame
675 * Helper function, updates stored data about game with given InstanceID
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};
681 HRESULT hr;
682 GAME_INSTALL_SCOPE installScope;
683 LPWSTR lpRegistryPath;
684 LPWSTR lpGDFBinaryPath, lpGameInstallDirectory;
686 TRACE("(%s)\n", debugstr_guid(InstanceID));
688 /* first, check is game exists in CURRENT_USER scope */
689 installScope = GIS_CURRENT_USER;
690 hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath);
692 if(hr == S_FALSE)
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);
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);
703 if(SUCCEEDED(hr))
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));
709 /* first, read required data about game */
710 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
711 sConfigGDFBinaryPath, &lpGDFBinaryPath);
713 if(SUCCEEDED(hr))
714 hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath,
715 sConfigApplicationPath, &lpGameInstallDirectory);
717 /* now remove currently existing registry key */
718 if(SUCCEEDED(hr))
719 hr = GAMEUX_RemoveRegistryRecord(InstanceID);
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);
726 HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath);
727 HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory);
730 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
731 TRACE("returning 0x%x\n", hr);
732 return hr;
734 /*******************************************************************************
735 * GAMEUX_FindGameInstanceId
737 * Internal helper function. Description available in gameux_private.h file
739 HRESULT GAMEUX_FindGameInstanceId(
740 LPCWSTR sGDFBinaryPath,
741 GAME_INSTALL_SCOPE installScope,
742 GUID* pInstanceId)
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};
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;
754 hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath);
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));
761 if(SUCCEEDED(hr))
763 hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL,
764 &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL));
766 if(SUCCEEDED(hr))
768 ++dwMaxSubKeyLen; /* for string terminator */
769 lpName = CoTaskMemAlloc(dwMaxSubKeyLen*sizeof(WCHAR));
770 if(!lpName) hr = E_OUTOFMEMORY;
773 if(SUCCEEDED(hr))
775 for(i=0; i<dwSubKeys && !found; ++i)
777 dwSubKeyLen = dwMaxSubKeyLen;
778 hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen,
779 NULL, NULL, NULL, NULL));
781 if(SUCCEEDED(hr))
782 hr = GAMEUX_LoadRegistryString(hRootKey, lpName,
783 sConfigGDFBinaryPath, &lpValue);
785 if(SUCCEEDED(hr))
786 if(lstrcmpW(lpValue, sGDFBinaryPath)==0)
788 /* key found, let's copy instance id and exit */
789 hr = (GUIDFromStringW(lpName, pInstanceId) ? S_OK : E_FAIL);
790 found = TRUE;
792 HeapFree(GetProcessHeap(), 0, lpValue);
796 HeapFree(GetProcessHeap(), 0, lpName);
797 RegCloseKey(hRootKey);
800 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
802 if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
803 hr = S_FALSE;
805 return hr;
807 /*******************************************************************************
808 * GameExplorer implementation
811 typedef struct _GameExplorerImpl
813 IGameExplorer IGameExplorer_iface;
814 IGameExplorer2 IGameExplorer2_iface;
815 LONG ref;
816 } GameExplorerImpl;
818 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
820 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface);
823 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
825 return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface);
828 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
829 IGameExplorer *iface,
830 REFIID riid,
831 void **ppvObject)
833 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
835 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
837 *ppvObject = NULL;
839 if(IsEqualGUID(riid, &IID_IUnknown) ||
840 IsEqualGUID(riid, &IID_IGameExplorer))
842 *ppvObject = &This->IGameExplorer_iface;
844 else if(IsEqualGUID(riid, &IID_IGameExplorer2))
846 *ppvObject = &This->IGameExplorer2_iface;
848 else
850 FIXME("interface %s not implemented\n", debugstr_guid(riid));
851 return E_NOINTERFACE;
854 IGameExplorer_AddRef(iface);
855 return S_OK;
858 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
860 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
861 LONG ref;
863 ref = InterlockedIncrement(&This->ref);
865 TRACE("(%p): ref=%d\n", This, ref);
866 return ref;
869 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
871 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
872 LONG ref;
874 ref = InterlockedDecrement(&This->ref);
875 TRACE("(%p): ref=%d\n", This, ref);
877 if(ref == 0)
879 TRACE("freeing GameExplorer object\n");
880 HeapFree(GetProcessHeap(), 0, This);
883 return ref;
886 static HRESULT WINAPI GameExplorerImpl_AddGame(
887 IGameExplorer *iface,
888 BSTR bstrGDFBinaryPath,
889 BSTR sGameInstallDirectory,
890 GAME_INSTALL_SCOPE installScope,
891 GUID *pInstanceID)
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);
898 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
899 IGameExplorer *iface,
900 GUID instanceID)
902 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
904 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
905 return GAMEUX_RemoveRegistryRecord(&instanceID);
908 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
909 IGameExplorer *iface,
910 GUID instanceID)
912 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
914 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
915 return GAMEUX_UpdateGame(&instanceID);
918 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
919 IGameExplorer *iface,
920 BSTR sGDFBinaryPath,
921 BOOL *pHasAccess)
923 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
925 FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
926 *pHasAccess = TRUE;
927 return S_OK;
930 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
932 GameExplorerImpl_QueryInterface,
933 GameExplorerImpl_AddRef,
934 GameExplorerImpl_Release,
935 GameExplorerImpl_AddGame,
936 GameExplorerImpl_RemoveGame,
937 GameExplorerImpl_UpdateGame,
938 GameExplorerImpl_VerifyAccess
942 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
943 IGameExplorer2 *iface,
944 REFIID riid,
945 void **ppvObject)
947 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
948 return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject);
951 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
953 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
954 return GameExplorerImpl_AddRef(&This->IGameExplorer_iface);
957 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
959 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
960 return GameExplorerImpl_Release(&This->IGameExplorer_iface);
963 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
964 IGameExplorer2 *iface,
965 LPCWSTR binaryGDFPath,
966 BOOL *pHasAccess)
968 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
969 FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
970 return E_NOTIMPL;
973 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
974 IGameExplorer2 *iface,
975 LPCWSTR binaryGDFPath,
976 LPCWSTR installDirectory,
977 GAME_INSTALL_SCOPE installScope)
979 HRESULT hr;
980 GUID instanceId;
981 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
983 TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
985 if(!binaryGDFPath)
986 return E_INVALIDARG;
988 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
990 if(hr == S_FALSE)
991 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
993 if(hr == S_FALSE)
995 /* if game isn't yet registered, then install it */
996 instanceId = GUID_NULL;
997 hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId);
999 else if(hr == S_OK)
1000 /* otherwise, update game */
1001 hr = GAMEUX_UpdateGame(&instanceId);
1003 return hr;
1006 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
1007 IGameExplorer2 *iface,
1008 LPCWSTR binaryGDFPath)
1010 HRESULT hr;
1011 GUID instanceId;
1012 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
1013 TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath));
1015 if(!binaryGDFPath)
1016 return E_INVALIDARG;
1018 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId);
1020 if(hr == S_FALSE)
1021 hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId);
1023 if(hr == S_OK)
1024 hr = GAMEUX_RemoveRegistryRecord(&instanceId);
1026 return hr;
1029 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
1031 GameExplorer2Impl_QueryInterface,
1032 GameExplorer2Impl_AddRef,
1033 GameExplorer2Impl_Release,
1034 GameExplorer2Impl_InstallGame,
1035 GameExplorer2Impl_UninstallGame,
1036 GameExplorer2Impl_CheckAccess
1040 * Construction routine
1042 HRESULT GameExplorer_create(
1043 IUnknown* pUnkOuter,
1044 IUnknown** ppObj)
1046 GameExplorerImpl *pGameExplorer;
1048 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1050 pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
1052 if(!pGameExplorer)
1053 return E_OUTOFMEMORY;
1055 pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl;
1056 pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl;
1057 pGameExplorer->ref = 1;
1059 *ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface;
1061 TRACE("returning iface: %p\n", *ppObj);
1062 return S_OK;